<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 4.2.1">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/%E6%AD%A6%E6%B1%8932x32.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/%E6%AD%A6%E6%B1%8916x16.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib1/font-awesome/css/all.min.css">
  <link rel="stylesheet" href="/lib1/pace/pace-theme-center-simple.min.css">
  <script src="/lib1/pace/pace.min.js"></script>

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"example.com","root":"/","scheme":"Gemini","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":true,"show_result":true,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},"path":"search.xml"};
  </script>

  <meta property="og:type" content="website">
<meta property="og:title" content="Technological Blog">
<meta property="og:url" content="http://example.com/page/8/index.html">
<meta property="og:site_name" content="Technological Blog">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="Li Yudong">
<meta name="twitter:card" content="summary">

<link rel="canonical" href="http://example.com/page/8/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : true,
    isPost : false,
    lang   : 'zh-CN'
  };
</script>

  <title>Technological Blog</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">Technological Blog</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
      <p class="site-subtitle" itemprop="description">IT小白的成长之旅</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-about">

    <a href="/about/" rel="section"><i class="user fa-fw"></i>关于</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/tags/" rel="section"><i class="tags fa-fw"></i>标签<span class="badge">18</span></a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="th fa-fw"></i>分类<span class="badge">14</span></a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/archives/" rel="section"><i class="archive fa-fw"></i>归档<span class="badge">95</span></a>

  </li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup">
        <div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div id="search-result">
  <div id="no-result">
    <i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
  </div>
</div>

    </div>
  </div>

</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

  <a href="https://github.com/tiarmor1" class="github-corner" title="Follow me on GitHub" aria-label="Follow me on GitHub" rel="noopener" target="_blank"><svg width="80" height="80" viewBox="0 0 250 250" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content index posts-expand">
            
      
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2020/09/29/%E7%AE%97%E6%B3%95/%E5%8F%8C%E6%8C%87%E9%92%88%E6%9F%A5%E6%89%BE/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/author.jpg">
      <meta itemprop="name" content="Li Yudong">
      <meta itemprop="description" content="">
    </span>
    
    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Technological Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2020/09/29/%E7%AE%97%E6%B3%95/%E5%8F%8C%E6%8C%87%E9%92%88%E6%9F%A5%E6%89%BE/" class="post-title-link" itemprop="url">双指针查找与常用算法框架</a>
        </h2>
    
        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
    
              <time title="创建时间：2020-09-29 17:30:23" itemprop="dateCreated datePublished" datetime="2020-09-29T17:30:23+08:00">2020-09-29</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2021-11-03 16:29:48" itemprop="dateModified" datetime="2021-11-03T16:29:48+08:00">2021-11-03</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E7%AE%97%E6%B3%95/" itemprop="url" rel="index"><span itemprop="name">算法</span></a>
                </span>
            </span>
    
          
    
        </div>
      </header>
    
    
    
    
    <div class="post-body" itemprop="articleBody">
    
      
          <h1 id="双指针技巧详解"><a href="#双指针技巧详解" class="headerlink" title="双指针技巧详解"></a>双指针技巧详解</h1><p>主要分为两类：1、快慢指针；2、左右指针；前者主要解决链表中的问题，比如典型的判定链表中是否包含环；后者主要解决数组（或字符串）中的问题，比如：二分查找。</p>
<p>链表、子串、数组的题目，一般都需要用到双指针技巧。</p>
<p>链表指针数组题，用双指针别犹豫。</p>
<p>双指针家三兄弟，各个都是万人迷。</p>
<p>快慢指针最神奇，链表操作无压力。</p>
<p>归并排序找中点，链表成环搞判定。</p>
<p>左右指针最常见，左右两端相向行。</p>
<p>反转数组要靠它，二分搜索是弟弟。</p>
<p>滑动窗口老猛男，子串问题全靠它。</p>
<p>左右指针滑窗口，一前一后齐头进。</p>
<h2 id="快慢指针的用法"><a href="#快慢指针的用法" class="headerlink" title="快慢指针的用法"></a>快慢指针的用法</h2><p>快慢指针一般都初始化指向链表的头节点head，前进时快指针fast在前，慢指针slow在后，从而巧妙解决链表中问题。</p>
<p><img src="/image/%E5%8F%8C%E6%8C%87%E9%92%88%E6%9F%A5%E6%89%BE/image-20210909211423726.png" alt="image-20210909211423726"></p>
<h3 id="判定链表中是否有环"><a href="#判定链表中是否有环" class="headerlink" title="判定链表中是否有环"></a>判定链表中是否有环</h3><p>单链表的特点是每个节点只知道下⼀个节点，所以⼀个指针的话⽆法判断链 表中是否含有环的。 如果链表中不含环，那么这个指针最终会遇到空指针 null 表⽰链表到头 了，这还好说，可以判断该链表不含环。但是如果链表中含有环，那么这个指针就会陷⼊死循环，因为环形数组中没 有 null 指针作为尾部节点。 </p>
<p>经典解法就是⽤两个指针，⼀个跑得快，⼀个跑得慢。如果不含有环，跑得 快的那个指针最终会遇到 null，说明链表不含环；如果含有环，快指针最终 会超慢指针⼀圈，和慢指针相遇，说明链表含有环。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">boolean</span> <span class="title">hasCycle</span><span class="params">(ListNode head)</span> </span>&#123; </span><br><span class="line">    ListNode fast, slow; </span><br><span class="line">    fast = slow = head; </span><br><span class="line">    <span class="keyword">while</span> (fast != null &amp;&amp; fast.next != null) &#123; </span><br><span class="line">        fast = fast.next.next; </span><br><span class="line">        slow = slow.next; </span><br><span class="line">        <span class="keyword">if</span> (fast == slow) <span class="keyword">return</span> <span class="literal">true</span>; </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="已知链表中有环，返回其起始位置"><a href="#已知链表中有环，返回其起始位置" class="headerlink" title="已知链表中有环，返回其起始位置"></a>已知链表中有环，返回其起始位置</h3><p>可以看到，当快慢指针相遇时，让其中任⼀个指针指向头节点，然后让它俩 以相同速度前进，再次相遇时所在的节点位置就是环开始的位置。</p>
<p>第⼀次相遇时，假设慢指针 slow ⾛了 k 步，那么快指针 fast ⼀定⾛了 2k 步，也就是说⽐ slow 多⾛了 k 步（也就是环的⻓度）。设相遇点距环的起点的距离为 m，那么环的起点距头结点 head 的距离为 k - m，也就是说如果从 head 前进 k - m 步就能到达环起点。 巧的是，如果从相遇点继续前进 k - m 步，也恰好到达环起点。 所以，只要我们把快慢指针中的任⼀个重新指向 head，然后两个指针同速 前进，k - m 步后就会相遇，相遇之处就是环的起点了</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">ListNode <span class="title">detectCycle</span><span class="params">(ListNode head)</span> </span>&#123; </span><br><span class="line">    ListNode fast, slow; </span><br><span class="line">    fast = slow = head; </span><br><span class="line">    <span class="keyword">while</span> (fast != null &amp;&amp; fast.next != null) &#123; </span><br><span class="line">        fast = fast.next.next; </span><br><span class="line">        slow = slow.next; </span><br><span class="line">        <span class="keyword">if</span> (fast == slow) </span><br><span class="line">            <span class="keyword">break</span>; </span><br><span class="line">    &#125;<span class="comment">// 上⾯的代码类似 hasCycle 函数</span></span><br><span class="line">    	slow = head; </span><br><span class="line">    <span class="keyword">while</span> (slow != fast) &#123; </span><br><span class="line">        fast = fast.next; </span><br><span class="line">        slow = slow.next; </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> slow; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="寻找链表的中点"><a href="#寻找链表的中点" class="headerlink" title="寻找链表的中点"></a>寻找链表的中点</h3><p>类似上⾯的思路，我们还可以让快指针⼀次前进两步，慢指针⼀次前进⼀ 步，当快指针到达链表尽头时，慢指针就处于链表的中间位置。当链表的⻓度是奇数时，slow 恰巧停在中点位置；如果⻓度是偶数，slow 最终的位置是中间偏右。</p>
<p>寻找链表中点的⼀个重要作⽤是对链表进⾏归并排序。 回想数组的归并排序：求中点索引递归地把数组⼆分，最后合并两个有序数 组。对于链表，合并两个有序链表是很简单的，难点就在于⼆分。 但是现在你学会了找到链表的中点，就能实现链表的⼆分了。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span> (fast != null &amp;&amp; fast.next != null) &#123; </span><br><span class="line">    fast = fast.next.next;</span><br><span class="line">    slow = slow.next; </span><br><span class="line">&#125;<span class="comment">// slow 就在中间位置 </span></span><br><span class="line"><span class="keyword">return</span> slow;</span><br></pre></td></tr></table></figure>

<h3 id="寻找链表的倒数第k个元素"><a href="#寻找链表的倒数第k个元素" class="headerlink" title="寻找链表的倒数第k个元素"></a>寻找链表的倒数第k个元素</h3><p>我们的思路还是使⽤快慢指针，让快指针先⾛ k 步，然后快慢指针开始同速 前进。这样当快指针⾛到链表末尾 null 时，慢指针所在的位置就是倒数第 k 个链表节点。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">ListNode slow, fast; </span><br><span class="line">slow = fast = head; </span><br><span class="line"><span class="keyword">while</span> (k-- &gt; <span class="number">0</span>) </span><br><span class="line">    fast = fast.next; </span><br><span class="line"><span class="keyword">while</span> (fast != null) &#123; </span><br><span class="line">    slow = slow.next; </span><br><span class="line">    fast = fast.next; &#125;</span><br><span class="line"><span class="keyword">return</span> slow;</span><br></pre></td></tr></table></figure>

<h2 id=""><a href="#" class="headerlink" title=""></a></h2><h2 id="左右指针的常见用法"><a href="#左右指针的常见用法" class="headerlink" title="左右指针的常见用法"></a>左右指针的常见用法</h2><p>左右指针在数组中实际是指两个索引值，⼀般初始化为 left = 0, right = nums.length - 1 。 </p>
<h3 id="二分查找"><a href="#二分查找" class="headerlink" title="二分查找"></a>二分查找</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">binarySearch</span><span class="params">(<span class="keyword">int</span>[] nums, <span class="keyword">int</span> target)</span> </span>&#123; </span><br><span class="line">    <span class="keyword">int</span> left = <span class="number">0</span>; </span><br><span class="line">    <span class="keyword">int</span> right = nums.length - <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">while</span>(left &lt;= right) &#123; </span><br><span class="line">        <span class="keyword">int</span> mid = (right + left) / <span class="number">2</span>; </span><br><span class="line">        <span class="keyword">if</span>(nums[mid] == target) </span><br><span class="line">            <span class="keyword">return</span> mid; </span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (nums[mid] &lt; target) </span><br><span class="line">            left = mid + <span class="number">1</span>; </span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (nums[mid] &gt; target) </span><br><span class="line">            right = mid - <span class="number">1</span>; </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">-1</span>; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="二数之和"><a href="#二数之和" class="headerlink" title="二数之和"></a>二数之和</h3><p>给定一个已经按照升序排列的有序数组，找到两个数使得它们相加之和等于目标数。函数应该返回两个下标值index1和index2。</p>
<p>只要数组有序就应该想到双指针用法，解法类似于二分查找，通过调节left和right来调整sum的大小。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span>[] twoSum(<span class="keyword">int</span>[] nums, <span class="keyword">int</span> target) &#123; </span><br><span class="line">    <span class="keyword">int</span> left = <span class="number">0</span>, right = nums.length - <span class="number">1</span>; </span><br><span class="line">    <span class="keyword">while</span> (left &lt; right) &#123; </span><br><span class="line">        <span class="keyword">int</span> sum = nums[left] + nums[right]; </span><br><span class="line">        <span class="keyword">if</span> (sum == target) &#123; <span class="comment">// 题⽬要求的索引是从 1 开始的</span></span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="keyword">int</span>[]&#123;left + <span class="number">1</span>, right + <span class="number">1</span>&#125;; </span><br><span class="line">        &#125; </span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (sum &lt; target) &#123; </span><br><span class="line">            left++; </span><br><span class="line">            <span class="comment">// 让 sum ⼤⼀点 </span></span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (sum &gt; target) &#123; </span><br><span class="line">            right--; </span><br><span class="line">            <span class="comment">// 让 sum ⼩⼀点 </span></span><br><span class="line">        &#125; </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="keyword">int</span>[]&#123;<span class="number">-1</span>, <span class="number">-1</span>&#125;; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="反转数组"><a href="#反转数组" class="headerlink" title="反转数组"></a>反转数组</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">reverse</span><span class="params">(<span class="keyword">int</span>[] nums)</span> </span>&#123; </span><br><span class="line">    <span class="keyword">int</span> left = <span class="number">0</span>; </span><br><span class="line">    <span class="keyword">int</span> right = nums.length - <span class="number">1</span>; </span><br><span class="line">    <span class="keyword">while</span> (left &lt; right) &#123; </span><br><span class="line">        <span class="comment">// swap(nums[left], nums[right]) </span></span><br><span class="line">        <span class="keyword">int</span> temp = nums[left]; </span><br><span class="line">        nums[left] = nums[right]; </span><br><span class="line">        nums[right] = temp; </span><br><span class="line">        left++; </span><br><span class="line">        right--; </span><br><span class="line">    &#125; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="滑动窗口算法"><a href="#滑动窗口算法" class="headerlink" title="滑动窗口算法"></a>滑动窗口算法</h2><p>滑动窗口是双指针技巧的最高境界，可以解决一大类子字符串匹配的问题，比如：最小覆盖子串、字符串的排列、找到字符串中所有字母异位词、无重复字符的最长子串。其实算法的技巧思路特别简单，即维护一个窗口，不断滑动并更新答案。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> left = <span class="number">0</span>, right = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">while</span> (right &lt; s.<span class="built_in">size</span>()) &#123;</span><br><span class="line">    <span class="comment">// 增⼤窗⼝ </span></span><br><span class="line">    window.add(s[right]); </span><br><span class="line">    right++; </span><br><span class="line">    </span><br><span class="line">    <span class="keyword">while</span> (window needs shrink) &#123; </span><br><span class="line">        <span class="comment">// 缩⼩窗⼝ </span></span><br><span class="line">        window.<span class="built_in">remove</span>(s[left]); </span><br><span class="line">        left++; </span><br><span class="line">    &#125; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这个算法技巧的时间复杂度是 O(N)，⽐字符串暴⼒算法要⾼效得多。 其实困扰⼤家的，不是算法的思路，⽽是各种细节问题。⽐如说如何向窗⼝ 中添加新元素，如何缩⼩窗⼝，在窗⼝滑动的哪个阶段更新结果。即便你明 ⽩了这些细节，也容易出 bug，找 bug 还不知道怎么找，真的挺让⼈⼼烦 的。</p>
<p><strong>所以今天我就写⼀套滑动窗⼝算法的代码框架，我连再哪⾥做输出</strong> <strong>debug</strong> <strong>都给你写好了，以后遇到相关的问题，你就默写出来如下框架然后改三个地</strong> <strong>⽅就⾏，还不会出</strong> <strong>bug</strong>： </p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 滑动窗口算法框架 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">slidingWindow</span><span class="params">(<span class="built_in">string</span> s, <span class="built_in">string</span> t)</span> </span>&#123;</span><br><span class="line">    <span class="built_in">unordered_map</span>&lt;<span class="keyword">char</span>, <span class="keyword">int</span>&gt; need, window;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">char</span> c : t) need[c]++;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> left = <span class="number">0</span>, right = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">int</span> valid = <span class="number">0</span>; </span><br><span class="line">    <span class="keyword">while</span> (right &lt; s.<span class="built_in">size</span>()) &#123;</span><br><span class="line">        <span class="comment">// c 是将移入窗口的字符</span></span><br><span class="line">        <span class="keyword">char</span> c = s[right];</span><br><span class="line">        <span class="comment">// 右移窗口</span></span><br><span class="line">        right++;</span><br><span class="line">        <span class="comment">// 进行窗口内数据的一系列更新</span></span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">        <span class="comment">/*** debug 输出的位置 ***/</span></span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">"window: [%d, %d)\n"</span>, left, right);</span><br><span class="line">        <span class="comment">/********************/</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 判断左侧窗口是否要收缩</span></span><br><span class="line">        <span class="keyword">while</span> (window needs shrink) &#123;</span><br><span class="line">            <span class="comment">// d 是将移出窗口的字符</span></span><br><span class="line">            <span class="keyword">char</span> d = s[left];</span><br><span class="line">            <span class="comment">// 左移窗口</span></span><br><span class="line">            left++;</span><br><span class="line">            <span class="comment">// 进行窗口内数据的一系列更新</span></span><br><span class="line">            ...</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>而且，这两个<code>...</code>处的操作分别是右移和左移窗口更新操作，等会你会发现它们操作是完全对称的。本文代码为 C++ 实现，不会用到什么编程方面的奇技淫巧，但是还是简单介绍一下一些用到的数据结构，以免有的读者因为语言的细节问题阻碍对算法思想的理解：</p>
<p><code>unordered_map</code>就是哈希表（字典），它的一个方法<code>count(key)</code>相当于 Java 的<code>containsKey(key)</code>可以判断键 key 是否存在。可以使用方括号访问键对应的值<code>map[key]</code>。需要注意的是，如果该<code>key</code>不存在，C++ 会自动创建这个 key，并把<code>map[key]</code>赋值为 0。所以代码中多次出现的<code>map[key]++</code>相当于 Java 的<code>map.put(key, map.getOrDefault(key, 0) + 1)</code>。</p>
<h3 id="最小覆盖子串"><a href="#最小覆盖子串" class="headerlink" title="最小覆盖子串"></a>最小覆盖子串</h3><p>题目：给你一个字符串S、字符串T，请在字符串S中找出：包含T所有字母的最小子串。</p>
<p>就是说要在<code>S</code>(source) 中找到包含<code>T</code>(target) 中全部字母的一个子串，且这个子串一定是所有可能子串中最短的。</p>
<p>如果我们使用暴力解法，代码大概是这样的：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; s.<span class="built_in">size</span>(); i++)</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> j = i + <span class="number">1</span>; j &lt; s.<span class="built_in">size</span>(); j++)</span><br><span class="line">        <span class="keyword">if</span> s[i:j] 包含 t 的所有字母:</span><br><span class="line">            更新答案</span><br></pre></td></tr></table></figure>

<p>而滑动窗口的思路是这样的：</p>
<p><em><strong>1、</strong></em>我们在字符串<code>S</code>中使用双指针中的左右指针技巧，初始化<code>left = right = 0</code>，把索引左闭右开区间<code>[left, right)</code>称为一个「窗口」。</p>
<p><em><strong>2、</strong></em>我们先不断地增加<code>right</code>指针扩大窗口<code>[left, right)</code>，直到窗口中的字符串符合要求（包含了<code>T</code>中的所有字符）。</p>
<p><em><strong>3、</strong></em>此时，我们停止增加<code>right</code>，转而不断增加<code>left</code>指针缩小窗口<code>[left, right)</code>，直到窗口中的字符串不再符合要求（不包含<code>T</code>中的所有字符了）。同时，每次增加<code>left</code>，我们都要更新一轮结果。</p>
<p><em><strong>4、</strong></em>重复第 2 和第 3 步，直到<code>right</code>到达字符串<code>S</code>的尽头</p>
<p>这个思路其实也不难，<strong>第 2 步相当于在寻找一个「可行解」，然后第 3 步在优化这个「可行解」，最终找到最优解，</strong>也就是最短的覆盖子串。左右指针轮流前进，窗口大小增增减减，窗口不断向右滑动，这就是「滑动窗口」这个名字的来历。下面画图理解一下，<code>needs</code>和<code>window</code>相当于计数器，分别记录<code>T</code>中字符出现次数和「窗口」中的相应字符的出现次数。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//首先，初始化window和need两个哈希表，记录窗口中的字符和需要凑齐的字符：</span></span><br><span class="line"><span class="built_in">unordered_map</span>&lt;<span class="keyword">char</span>, <span class="keyword">int</span>&gt; need, window;</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">char</span> c : t) need[c]++;<span class="comment">//为每个键值赋值</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//然后，使用left和right变量初始化窗口的两端，不要忘了，区间[left, right)是左闭右开的，所以初始情况下窗口没有包含任何元素：</span></span><br><span class="line"><span class="keyword">int</span> left = <span class="number">0</span>, right = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">int</span> valid = <span class="number">0</span>; </span><br><span class="line"><span class="keyword">while</span> (right &lt; s.<span class="built_in">size</span>()) &#123;</span><br><span class="line">    <span class="comment">// 开始滑动</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//其中valid变量表示窗口中满足need条件的字符个数，如果valid和need.size的大小相同，则说明窗口已满足条件，已经完全覆盖了串T。</span></span><br><span class="line"><span class="comment">//现在开始套模板，只需要思考以下四个问题：</span></span><br><span class="line"><span class="comment">//1、当移动right扩大窗口，即加入字符时，应该更新哪些数据？</span></span><br><span class="line"><span class="comment">//2、什么条件下，窗口应该暂停扩大，开始移动left缩小窗口？</span></span><br><span class="line"><span class="comment">//3、当移动left缩小窗口，即移出字符时，应该更新哪些数据？</span></span><br><span class="line"><span class="comment">//4、我们要的结果应该在扩大窗口时还是缩小窗口时进行更新？</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//如果一个字符进入窗口，应该增加window计数器；如果一个字符将移出窗口的时候，应该减少window计数器；当valid满足need时应该收缩窗口；应该在收缩窗口的时候更新最终结果。</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="built_in">string</span> <span class="title">minWindow</span><span class="params">(<span class="built_in">string</span> s, <span class="built_in">string</span> t)</span> </span>&#123;</span><br><span class="line">    <span class="built_in">unordered_map</span>&lt;<span class="keyword">char</span>, <span class="keyword">int</span>&gt; need, window;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">char</span> c : t) need[c]++;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> left = <span class="number">0</span>, right = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">int</span> valid = <span class="number">0</span>;</span><br><span class="line">    <span class="comment">// 记录最小覆盖子串的起始索引及长度</span></span><br><span class="line">    <span class="keyword">int</span> start = <span class="number">0</span>, len = INT_MAX;</span><br><span class="line">    <span class="keyword">while</span> (right &lt; s.<span class="built_in">size</span>()) &#123;</span><br><span class="line">        <span class="comment">// c 是将移入窗口的字符</span></span><br><span class="line">        <span class="keyword">char</span> c = s[right];</span><br><span class="line">        <span class="comment">// 右移窗口</span></span><br><span class="line">        right++;</span><br><span class="line">        <span class="comment">// 进行窗口内数据的一系列更新</span></span><br><span class="line">        <span class="keyword">if</span> (need.count(c)) &#123;</span><br><span class="line">            window[c]++;</span><br><span class="line">            <span class="keyword">if</span> (window[c] == need[c])</span><br><span class="line">                valid++;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 判断左侧窗口是否要收缩</span></span><br><span class="line">        <span class="keyword">while</span> (valid == need.<span class="built_in">size</span>()) &#123;</span><br><span class="line">            <span class="comment">// 在这里更新最小覆盖子串</span></span><br><span class="line">            <span class="keyword">if</span> (right - left &lt; len) &#123;</span><br><span class="line">                start = left;</span><br><span class="line">                len = right - left;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// d 是将移出窗口的字符</span></span><br><span class="line">            <span class="keyword">char</span> d = s[left];</span><br><span class="line">            <span class="comment">// 左移窗口</span></span><br><span class="line">            left++;</span><br><span class="line">            <span class="comment">// 进行窗口内数据的一系列更新</span></span><br><span class="line">            <span class="keyword">if</span> (need.count(d)) &#123;</span><br><span class="line">                <span class="keyword">if</span> (window[d] == need[d])</span><br><span class="line">                    valid--;</span><br><span class="line">                window[d]--;</span><br><span class="line">            &#125;                    </span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 返回最小覆盖子串</span></span><br><span class="line">    <span class="keyword">return</span> len == INT_MAX ?</span><br><span class="line">        <span class="string">""</span> : s.substr(start, len);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>需要注意的是，当我们发现某个字符在<code>window</code>的数量满足了<code>need</code>的需要，就要更新<code>valid</code>，表示有一个字符已经满足要求。而且，你能发现，两次对窗口内数据的更新操作是完全对称的。</p>
<p>当<code>valid == need.size()</code>时，说明<code>T</code>中所有字符已经被覆盖，已经得到一个可行的覆盖子串，现在应该开始收缩窗口了，以便得到「最小覆盖子串」。移动<code>left</code>收缩窗口时，窗口内的字符都是可行解，所以应该在收缩窗口的阶段进行最小覆盖子串的更新，以便从可行解中找到长度最短的最终结果。</p>
<p>至此，应该可以完全理解这套框架了，滑动窗口算法又不难，就是细节问题让人烦得很。<strong>以后遇到滑动窗口算法，你就按照这框架写代码，保准没有 bug，还省事儿</strong>。</p>
<h3 id="字符串排列"><a href="#字符串排列" class="headerlink" title="字符串排列"></a>字符串排列</h3><p>题目：给定两个字符串S1、S2，写一个函数来判断S2是否包含S1的序列；即第一个字符串的排列之一是第二个字符串的子串</p>
<p>这种题目，是明显的滑动窗口算法，<strong>相当给你一个<code>S</code>和一个<code>T</code>，请问你<code>S</code>中是否存在一个子串，包含<code>T</code>中所有字符且不包含其他字符</strong>？首先，先复制粘贴之前的算法框架代码，然后明确刚才提出的 4 个问题，即可写出这道题的答案：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 判断 s 中是否存在 t 的排列</span></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">checkInclusion</span><span class="params">(<span class="built_in">string</span> t, <span class="built_in">string</span> s)</span> </span>&#123;</span><br><span class="line">    <span class="built_in">unordered_map</span>&lt;<span class="keyword">char</span>, <span class="keyword">int</span>&gt; need, window;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">char</span> c : t) need[c]++;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> left = <span class="number">0</span>, right = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">int</span> valid = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (right &lt; s.<span class="built_in">size</span>()) &#123;</span><br><span class="line">        <span class="keyword">char</span> c = s[right];</span><br><span class="line">        right++;</span><br><span class="line">        <span class="comment">// 进行窗口内数据的一系列更新</span></span><br><span class="line">        <span class="keyword">if</span> (need.count(c)) &#123;</span><br><span class="line">            window[c]++;</span><br><span class="line">            <span class="keyword">if</span> (window[c] == need[c])</span><br><span class="line">                valid++;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 判断左侧窗口是否要收缩</span></span><br><span class="line">        <span class="keyword">while</span> (right - left &gt;= t.<span class="built_in">size</span>()) &#123;</span><br><span class="line">            <span class="comment">// 在这里判断是否找到了合法的子串</span></span><br><span class="line">            <span class="keyword">if</span> (valid == need.<span class="built_in">size</span>())</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">            <span class="keyword">char</span> d = s[left];</span><br><span class="line">            left++;</span><br><span class="line">            <span class="comment">// 进行窗口内数据的一系列更新</span></span><br><span class="line">            <span class="keyword">if</span> (need.count(d)) &#123;</span><br><span class="line">                <span class="keyword">if</span> (window[d] == need[d])</span><br><span class="line">                    valid--;</span><br><span class="line">                window[d]--;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 未找到符合条件的子串</span></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>对于这道题的解法代码，基本上和最小覆盖子串一模一样，只需要改变两个地方：</p>
<p><strong>1、</strong>本题移动<code>left</code>缩小窗口的时机是窗口大小大于<code>t.size()</code>时，因为排列嘛，显然长度应该是一样的。</p>
<p><strong>2、</strong>当发现<code>valid == need.size()</code>时，就说明窗口中就是一个合法的排列，所以立即返回<code>true</code>。</p>
<p>至于如何处理窗口的扩大和缩小，和最小覆盖子串完全相同。</p>
<h3 id="找所有字母异位词"><a href="#找所有字母异位词" class="headerlink" title="找所有字母异位词"></a>找所有字母异位词</h3><p>给定一个字符串s和非空字符串p，找到s中所有是p的字母异位词的子串，返回这些子串的起始索引。（字母异位词指字母相同，但排列不同的字符串；不考虑答案输出的顺序）</p>
<p>呵呵，这个所谓的字母异位词，不就是排列吗，搞个高端的说法就能糊弄人了吗？<strong>相当于，输入一个串<code>S</code>，一个串<code>T</code>，找到<code>S</code>中所有<code>T</code>的排列，返回它们的起始索引</strong>。直接默写一下框架，明确刚才讲的 4 个问题，即可秒杀这道题：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; <span class="title">findAnagrams</span><span class="params">(<span class="built_in">string</span> s, <span class="built_in">string</span> t)</span> </span>&#123;</span><br><span class="line">    <span class="built_in">unordered_map</span>&lt;<span class="keyword">char</span>, <span class="keyword">int</span>&gt; need, window;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">char</span> c : t) need[c]++;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> left = <span class="number">0</span>, right = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">int</span> valid = <span class="number">0</span>;</span><br><span class="line">    <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; res; <span class="comment">// 记录结果</span></span><br><span class="line">    <span class="keyword">while</span> (right &lt; s.<span class="built_in">size</span>()) &#123;</span><br><span class="line">        <span class="keyword">char</span> c = s[right];</span><br><span class="line">        right++;</span><br><span class="line">        <span class="comment">// 进行窗口内数据的一系列更新</span></span><br><span class="line">        <span class="keyword">if</span> (need.count(c)) &#123;</span><br><span class="line">            window[c]++;</span><br><span class="line">            <span class="keyword">if</span> (window[c] == need[c]) </span><br><span class="line">                valid++;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 判断左侧窗口是否要收缩</span></span><br><span class="line">        <span class="keyword">while</span> (right - left &gt;= t.<span class="built_in">size</span>()) &#123;</span><br><span class="line">            <span class="comment">// 当窗口符合条件时，把起始索引加入 res</span></span><br><span class="line">            <span class="keyword">if</span> (valid == need.<span class="built_in">size</span>())</span><br><span class="line">                res.push_back(left);</span><br><span class="line">            <span class="keyword">char</span> d = s[left];</span><br><span class="line">            left++;</span><br><span class="line">            <span class="comment">// 进行窗口内数据的一系列更新</span></span><br><span class="line">            <span class="keyword">if</span> (need.count(d)) &#123;</span><br><span class="line">                <span class="keyword">if</span> (window[d] == need[d])</span><br><span class="line">                    valid--;</span><br><span class="line">                window[d]--;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>跟寻找字符串的排列一样，只是找到一个合法异位词（排列）之后将起始索引加入<code>res</code>即可</p>
<h3 id="最长无重复子串"><a href="#最长无重复子串" class="headerlink" title="最长无重复子串"></a>最长无重复子串</h3><p>题目：给定一个字符串，找出其中不含有重复重复字符的的最长子串的长度。 </p>
<p>这个题终于有了点新意，不是一套框架就出答案，不过反而更简单了，稍微改一改框架就行了：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">lengthOfLongestSubstring</span><span class="params">(<span class="built_in">string</span> s)</span> </span>&#123;</span><br><span class="line">    <span class="built_in">unordered_map</span>&lt;<span class="keyword">char</span>, <span class="keyword">int</span>&gt; window;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> left = <span class="number">0</span>, right = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">int</span> res = <span class="number">0</span>; <span class="comment">// 记录结果</span></span><br><span class="line">    <span class="keyword">while</span> (right &lt; s.<span class="built_in">size</span>()) &#123;</span><br><span class="line">        <span class="keyword">char</span> c = s[right];</span><br><span class="line">        right++;</span><br><span class="line">        <span class="comment">// 进行窗口内数据的一系列更新</span></span><br><span class="line">        window[c]++;</span><br><span class="line">        <span class="comment">// 判断左侧窗口是否要收缩</span></span><br><span class="line">        <span class="keyword">while</span> (window[c] &gt; <span class="number">1</span>) &#123;</span><br><span class="line">            <span class="keyword">char</span> d = s[left];</span><br><span class="line">            left++;</span><br><span class="line">            <span class="comment">// 进行窗口内数据的一系列更新</span></span><br><span class="line">            window[d]--;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 在这里更新答案</span></span><br><span class="line">        res = <span class="built_in">max</span>(res, right - left);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这就是变简单了，连<code>need</code>和<code>valid</code>都不需要，而且更新窗口内数据也只需要简单的更新计数器<code>window</code>即可。</p>
<p>当<code>window[c]</code>值大于 1 时，说明窗口中存在重复字符，不符合条件，就该移动<code>left</code>缩小窗口了嘛。唯一需要注意的是，在哪里更新结果<code>res</code>呢？我们要的是最长无重复子串，哪一个阶段可以保证窗口中的字符串是没有重复的呢？</p>
<p>这里和之前不一样，**要在收缩窗口完成后更新<code>res</code>**，因为窗口收缩的 while 条件是存在重复元素，换句话说收缩完成后一定保证窗口中没有重复嘛。</p>
<h1 id="twoSum问题的核心思想"><a href="#twoSum问题的核心思想" class="headerlink" title="twoSum问题的核心思想"></a>twoSum问题的核心思想</h1><h2 id="第一题"><a href="#第一题" class="headerlink" title="第一题"></a>第一题</h2><p>这个问题的<strong>最基本形式</strong>是这样：给你⼀个数组和⼀个整数 target ，可以保 证数组中<strong>存在</strong>两个数的和为 target ，请你返回这两个数的索引。这个问题如何解决呢？⾸先最简单粗暴的办法当然是穷举了：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span>[] twoSum(<span class="keyword">int</span>[] nums, <span class="keyword">int</span> target) &#123; </span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; nums.length; i++) </span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> j = i + <span class="number">1</span>; j &lt; nums.length; j++) </span><br><span class="line">            <span class="keyword">if</span> (nums[j] == target - nums[i]) </span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">new</span> <span class="keyword">int</span>[] &#123; i, j &#125;; </span><br><span class="line">    <span class="comment">// 不存在这么两个数 </span></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="keyword">int</span>[] &#123;<span class="number">-1</span>, <span class="number">-1</span>&#125;; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这个解法⾮常直接，时间复杂度 O(N^2)，空间复杂度 O(1)。 可以通过⼀个哈希表减少时间复杂度：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span>[] twoSum(<span class="keyword">int</span>[] nums, <span class="keyword">int</span> target) &#123; </span><br><span class="line">    <span class="keyword">int</span> n = nums.length; </span><br><span class="line">    index&lt;Integer, Integer&gt; </span><br><span class="line">        index = <span class="keyword">new</span> HashMap&lt;&gt;();</span><br><span class="line">    <span class="comment">// 构造⼀个哈希表：元素映射到相应的索引 </span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; n; i++) </span><br><span class="line">        index.<span class="built_in">put</span>(nums[i], i); </span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; n; i++) &#123; </span><br><span class="line">        <span class="keyword">int</span> other = target - nums[i]; </span><br><span class="line">        <span class="comment">// 如果 other 存在且不是 nums[i] 本⾝ </span></span><br><span class="line">        <span class="keyword">if</span> (index.containsKey(other) &amp;&amp; index.<span class="built_in">get</span>(other) != i) </span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="keyword">int</span>[] &#123;i, index.<span class="built_in">get</span>(other)&#125;; </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="keyword">int</span>[] &#123;<span class="number">-1</span>, <span class="number">-1</span>&#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这样，由于哈希表的查询时间为 O(1)，算法的时间复杂度降低到 O(N)，但 是需要 O(N) 的空间复杂度来存储哈希表。不过综合来看，是要⽐暴⼒解法 ⾼效的。 </p>
<p><strong>我觉得</strong> <strong>Two Sum</strong> <strong>系列问题就是想教我们如何使⽤哈希表处理问题</strong>。我们接 着往后看。 </p>
<h2 id="第二题"><a href="#第二题" class="headerlink" title="第二题"></a>第二题</h2><p>这⾥我们稍微修改⼀下上⾯的问题。我们设计⼀个类，拥有两个 API： </p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TwoSum</span> &#123;</span> </span><br><span class="line">    <span class="comment">// 向数据结构中添加⼀个数 number </span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> number)</span></span>; </span><br><span class="line">    <span class="comment">// 寻找当前数据结构中是否存在两个数的和为 value </span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">find</span><span class="params">(<span class="keyword">int</span> value)</span></span>; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>如何实现这两个 API 呢，我们可以仿照上⼀道题⽬，使⽤⼀个哈希表辅助 find ⽅法：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TwoSum</span> &#123;</span> </span><br><span class="line">    Map&lt;Integer, Integer&gt; freq = <span class="keyword">new</span> HashMap&lt;&gt;();</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> number)</span> </span>&#123; </span><br><span class="line">        <span class="comment">// 记录 number 出现的次数 </span></span><br><span class="line">        freq.<span class="built_in">put</span>(number, freq.getOrDefault(number, <span class="number">0</span>) + <span class="number">1</span>); </span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">find</span><span class="params">(<span class="keyword">int</span> value)</span> </span>&#123; </span><br><span class="line">        <span class="keyword">for</span> (Integer key : freq.keySet()) &#123; </span><br><span class="line">            <span class="keyword">int</span> other = value - key; </span><br><span class="line">            <span class="comment">// 情况⼀ </span></span><br><span class="line">            <span class="keyword">if</span> (other == key &amp;&amp; freq.<span class="built_in">get</span>(key) &gt; <span class="number">1</span>) </span><br><span class="line">                <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">            <span class="comment">// 情况⼆ </span></span><br><span class="line">            <span class="keyword">if</span> (other != key &amp;&amp; freq.containsKey(other)) </span><br><span class="line">                <span class="keyword">return</span> <span class="literal">true</span>; </span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>; </span><br><span class="line">    &#125; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>进⾏ find 的时候有两种情况，举个例⼦： </p>
<p>情况⼀： add 了 [3,3,2,5] 之后，执⾏ find(6) ，由于 3 出现了两次，3 + 3 = 6，所以返回 true。 </p>
<p>情况⼆： add 了 [3,3,2,5] 之后，执⾏ find(7) ，那么 key 为 2， other 为 5 时算法可以返回 true。 </p>
<p>除了上述两种情况外， find 只能返回 false 了。 </p>
<p>对于这个解法的时间复杂度呢， add ⽅法是 O(1)， find ⽅法是 O(N)，空 间复杂度为 O(N)，和上⼀道题⽬⽐较类似。<strong>但是对于</strong> <strong>API</strong> <strong>的设计，是需要考虑现实情况的</strong>。⽐如说，我们设计的这个 类，使⽤ find ⽅法⾮常频繁，那么每次都要 O(N) 的时间，岂不是很浪费 费时间吗？对于这种情况，我们是否可以做些优化呢？ </p>
<p>是的，对于频繁使⽤ find ⽅法的场景，我们可以进⾏优化。我们可以参 考上⼀道题⽬的暴⼒解法，借助<strong>哈希集合</strong>来针对性优化 find ⽅法：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TwoSum</span> &#123;</span></span><br><span class="line">    Set&lt;Integer&gt; sum = <span class="keyword">new</span> HashSet&lt;&gt;(); </span><br><span class="line">    List&lt;Integer&gt; nums = <span class="keyword">new</span> ArrayList&lt;&gt;(); </span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> number)</span> </span>&#123; </span><br><span class="line">        <span class="comment">// 记录所有可能组成的和 </span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> n : nums) </span><br><span class="line">            sum.add(n + number); </span><br><span class="line">        nums.add(number); </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">find</span><span class="params">(<span class="keyword">int</span> value)</span> </span>&#123; </span><br><span class="line">        <span class="keyword">return</span> sum.contains(value); </span><br><span class="line">    &#125; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这样 sum 中就储存了所有加⼊数字可能组成的和，每次 find 只要花费 O(1) 的时间在集合中判断⼀下是否存在就⾏了，显然⾮常适合频繁使⽤ find 的场景。</p>
<p>对于 TwoSum 问题，⼀个难点就是给的数组<strong>⽆序</strong>。对于⼀个⽆序的数组， 我们似乎什么技巧也没有，只能暴⼒穷举所有可能。 </p>
<p><strong>⼀般情况下，我们会⾸先把数组排序再考虑双指针技巧</strong>。TwoSum 启发我 们，HashMap 或者 HashSet 也可以帮助我们处理⽆序数组相关的简单问题。 另外，设计的核⼼在于权衡，利⽤不同的数据结构，可以得到⼀些针对性的 加强。 </p>
<p>最后，如果 TwoSum I 中给的数组是有序的，应该如何编写算法呢？答案很 简单，前⽂「双指针技巧汇总」写过：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span>[] twoSum(<span class="keyword">int</span>[] nums, <span class="keyword">int</span> target) &#123; </span><br><span class="line">    <span class="keyword">int</span> left = <span class="number">0</span>, right = nums.length - <span class="number">1</span>; </span><br><span class="line">    <span class="keyword">while</span> (left &lt; right) &#123; </span><br><span class="line">        <span class="keyword">int</span> sum = nums[left] + nums[right]; </span><br><span class="line">        <span class="keyword">if</span> (sum == target) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="keyword">int</span>[]&#123;left, right&#125;; </span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (sum &lt; target) &#123; </span><br><span class="line">            left++; <span class="comment">// 让 sum ⼤⼀点 </span></span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (sum &gt; target) &#123; </span><br><span class="line">            right--; <span class="comment">// 让 sum ⼩⼀点 </span></span><br><span class="line">        &#125; </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 不存在这样两个数 </span></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="keyword">int</span>[]&#123;<span class="number">-1</span>, <span class="number">-1</span>&#125;; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h1 id="二分查找详解"><a href="#二分查找详解" class="headerlink" title="二分查找详解"></a>二分查找详解</h1><p>⼆分查找并不简单，Knuth ⼤佬（发明 KMP 算法的那位）都说⼆分查找： </p>
<p><strong>思路很简单，细节是魔⿁</strong>。很多⼈喜欢拿整型溢出的 bug 说事⼉，但是⼆分 查找真正的坑根本就不是那个细节问题，⽽是在于到底要给 mid 加⼀还是 减⼀，while ⾥到底⽤ &lt;= 还是 &lt; 。 </p>
<h2 id="二分搜索诗"><a href="#二分搜索诗" class="headerlink" title="二分搜索诗"></a>二分搜索诗</h2><p>管他左侧还右侧，搜索区间定乾坤。</p>
<p>搜索一个元素时，搜索区间两端闭。</p>
<p>while条件带等号，否则需要打补丁。</p>
<p>if相等就返回，其他的事甭操心。</p>
<p>mid必须加减一，因为区间两端闭。</p>
<p>while结束就凉凉，凄凄惨惨返-1。</p>
<p>搜索左右边界时，搜索区间要阐明。</p>
<p>左闭右开最常见，其余逻辑便自明。</p>
<p>while要用小于号，这样才能不漏掉。</p>
<p>if相等别返回，利用mid锁边界。</p>
<p>mid加一或减一？要看区间区间开或闭。</p>
<p>while结束不算完，因为你还没返回。</p>
<p>索引可能出边界，if检查保平安。</p>
<h2 id="二分查找框架"><a href="#二分查找框架" class="headerlink" title="二分查找框架"></a>二分查找框架</h2><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">binarySearch</span><span class="params">(<span class="keyword">int</span>[] nums, <span class="keyword">int</span> target)</span> </span>&#123; </span><br><span class="line">    <span class="keyword">int</span> left = <span class="number">0</span>, right = ...; </span><br><span class="line">    <span class="keyword">while</span>(...) &#123; </span><br><span class="line">        <span class="keyword">int</span> mid = left + (right - left) / <span class="number">2</span>; </span><br><span class="line">        <span class="keyword">if</span> (nums[mid] == target) &#123; </span><br><span class="line">            ... </span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (nums[mid] &lt; target) &#123; </span><br><span class="line">            left = ... </span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (nums[mid] &gt; target) &#123; </span><br><span class="line">            right = ... </span><br><span class="line">        &#125; </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> ...; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>分析⼆分查找的⼀个技巧是：不要出现</strong> <strong>else****，⽽是把所有情况⽤</strong> <strong>else if</strong> <strong>写清</strong> <strong>楚，这样可以清楚地展现所有细节</strong>。本⽂都会使⽤ else if，旨在讲清楚。</p>
<p>其中 … 标记的部分，就是可能出现细节问题的地⽅，当你⻅到⼀个⼆分 查找的代码时，⾸先注意这⼏个地⽅。后⽂⽤实例分析这些地⽅能有什么样 的变化。 </p>
<p>另外声明⼀下，计算 mid 时需要防⽌溢出，代码中 left + (right - left) / 2 就和 (left + right) / 2 的结果相同，但是有效防⽌了 left 和right 太⼤直接相加导致溢出</p>
<h2 id="寻找一个数：基本二分查找"><a href="#寻找一个数：基本二分查找" class="headerlink" title="寻找一个数：基本二分查找"></a>寻找一个数：基本二分查找</h2><p>这个场景是最简单的，肯能也是⼤家最熟悉的，即搜索⼀个数，如果存在， 返回其索引，否则返回 -1。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">binarySearch</span><span class="params">(<span class="keyword">int</span>[] nums, <span class="keyword">int</span> target)</span> </span>&#123; </span><br><span class="line">    <span class="keyword">int</span> left = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">int</span> right = nums.length - <span class="number">1</span>; <span class="comment">// 注意 </span></span><br><span class="line">    <span class="keyword">while</span>(left &lt;= right) &#123; </span><br><span class="line">        <span class="keyword">int</span> mid = left + (right - left) / <span class="number">2</span>; </span><br><span class="line">        <span class="keyword">if</span>(nums[mid] == target) </span><br><span class="line">            <span class="keyword">return</span> mid; </span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (nums[mid] &lt; target) </span><br><span class="line">            left = mid + <span class="number">1</span>; <span class="comment">// 注意 </span></span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (nums[mid] &gt; target) </span><br><span class="line">            right = mid - <span class="number">1</span>; <span class="comment">// 注意 </span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">-1</span>; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>1****、为什么</strong> <strong>while</strong> <strong>循环的条件中是</strong> **&lt;=**<strong>，⽽不是</strong> **&lt;**？ </p>
<p>答：因为初始化 right 的赋值是 nums.length - 1 ，即最后⼀个元素的索 引，⽽不是 nums.length 。 </p>
<p>这⼆者可能出现在不同功能的⼆分查找中，区别是：前者相当于两端都闭区 间 [left, right] ，后者相当于左闭右开区间 [left, right) ，因为索引⼤⼩为 nums.length 是越界的。</p>
<p>我们这个算法中使⽤的是前者 [left, right] 两端都闭的区间。<strong>这个区间</strong> </p>
<p><strong>其实就是每次进⾏搜索的区间</strong>。 </p>
<p>2、什么时候应该停⽌搜索呢？</p>
<p>当然，找到了⽬标值的时候可以终⽌： </p>
<p>if(nums[mid] == target) return mid; </p>
<p>但如果没找到，就需要 while 循环终⽌，然后返回 -1。那 while 循环什么时 候应该终⽌？<strong>搜索区间为空的时候应该终⽌</strong>，意味着你没得找了，就等于没 找到嘛。 </p>
<p>while(left &lt;= right) 的终⽌条件是 left == right + 1 ，写成区间的形式 就是 [right + 1, right] ，或者带个具体的数字进去 [3, 2] ，可⻅<strong>这时候</strong> <strong>区间为空</strong>，</p>
<p>while(left &lt; right) 的终⽌条件是 left == right ，写成区间的形式就是 [left, right] ，或者带个具体的数字进去 [2, 2] ，<strong>这时候区间⾮空</strong>，还 有⼀个数 2，但此时 while 循环终⽌了。也就是说这区间 [2, 2] 被漏掉 了，索引 2 没有被搜索，如果这时候直接返回 -1 就是错误的。</p>
<p><strong>3****、为什么</strong> <strong>left = mid + 1</strong> <strong>，</strong> <strong>right = mid - 1</strong> <strong>？我看有的代码是</strong> <strong>right =</strong> <strong>mid</strong> <strong>或者</strong> <strong>left = mid</strong> <strong>，没有这些加加减减，到底怎么回事，怎么判断</strong>？ </p>
<p>答：这也是⼆分查找的⼀个难点，不过只要你能理解前⾯的内容，就能够很 容易判断。 </p>
<p>刚才明确了「搜索区间」这个概念，⽽且本算法的搜索区间是两端都闭的， 即 [left, right] 。那么当我们发现索引 mid 不是要找的 target 时，下 ⼀步应该去搜索哪⾥呢？ 当然是去搜索 [left, mid-1] 或者 [mid+1, right] 对不对？<strong>因为</strong> <strong>mid</strong> <strong>已</strong> <strong>经搜索过，应该从搜索区间中去除</strong>。</p>
<p><strong>4****、此算法有什么缺陷</strong>？ </p>
<p>答：⾄此，你应该已经掌握了该算法的所有细节，以及这样处理的原因。但 是，这个算法存在局限性。 </p>
<p>⽐如说给你有序数组 nums = [1,2,2,2,3] ， target 为 2，此算法返回的索 引是 2，没错。但是如果我想得到 target 的左侧边界，即索引 1，或者我 想得到 target 的右侧边界，即索引 3，这样的话此算法是⽆法处理的。</p>
<h2 id="寻找左侧边界的⼆分搜索"><a href="#寻找左侧边界的⼆分搜索" class="headerlink" title="寻找左侧边界的⼆分搜索"></a><strong>寻找左侧边界的⼆分搜索</strong></h2><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">left_bound</span><span class="params">(<span class="keyword">int</span>[] nums, <span class="keyword">int</span> target)</span> </span>&#123; </span><br><span class="line">    <span class="keyword">if</span> (nums.length == <span class="number">0</span>) <span class="keyword">return</span> <span class="number">-1</span>; </span><br><span class="line">    <span class="keyword">int</span> left = <span class="number">0</span>; </span><br><span class="line">    <span class="keyword">int</span> right = nums.length; <span class="comment">// 注意 </span></span><br><span class="line">    <span class="keyword">while</span> (left &lt; right) &#123; <span class="comment">// 注意 </span></span><br><span class="line">        <span class="keyword">int</span> mid = (left + right) / <span class="number">2</span>; </span><br><span class="line">        <span class="keyword">if</span> (nums[mid] == target) &#123; </span><br><span class="line">            right = mid; </span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (nums[mid] &lt; target) &#123; </span><br><span class="line">            left = mid + <span class="number">1</span>; </span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (nums[mid] &gt; target) &#123; </span><br><span class="line">            right = mid; <span class="comment">// 注意 </span></span><br><span class="line">        &#125; </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> left; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>1****、为什么</strong> <strong>while</strong> <strong>中是</strong> <strong>&lt;</strong> <strong>⽽不是</strong> <strong>&lt;=</strong> ? </p>
<p>答：⽤相同的⽅法分析，因为 right = nums.length ⽽不是 nums.length - 1 。因此每次循环的「搜索区间」是 [left, right) 左闭右开。 while(left &lt; right) 终⽌的条件是 left == right ，此时搜索区间 [left, left) 为空，所以可以正确终⽌。 </p>
<h2 id="寻找右侧边界的⼆分查找"><a href="#寻找右侧边界的⼆分查找" class="headerlink" title="寻找右侧边界的⼆分查找"></a><strong>寻找右侧边界的⼆分查找</strong></h2><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">right_bound</span><span class="params">(<span class="keyword">int</span>[] nums, <span class="keyword">int</span> target)</span> </span>&#123; </span><br><span class="line">    <span class="keyword">if</span> (nums.length == <span class="number">0</span>) <span class="keyword">return</span> <span class="number">-1</span>; </span><br><span class="line">    <span class="keyword">int</span> left = <span class="number">0</span>, right = nums.length; </span><br><span class="line">    </span><br><span class="line">    <span class="keyword">while</span> (left &lt; right) &#123; </span><br><span class="line">        <span class="keyword">int</span> mid = (left + right) / <span class="number">2</span>; </span><br><span class="line">        <span class="keyword">if</span> (nums[mid] == target) &#123; </span><br><span class="line">            left = mid + <span class="number">1</span>; <span class="comment">// 注意 </span></span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (nums[mid] &lt; target) &#123;</span><br><span class="line">            left = mid + <span class="number">1</span>; </span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (nums[mid] &gt; target) &#123; </span><br><span class="line">            right = mid; </span><br><span class="line">        &#125; </span><br><span class="line">    &#125;<span class="keyword">return</span> left - <span class="number">1</span>; <span class="comment">// 注意 </span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h1 id="字符串乘法"><a href="#字符串乘法" class="headerlink" title="字符串乘法"></a>字符串乘法</h1><p>对于⽐较⼩的数字，做运算可以直接使⽤编程语⾔提供的运算符，但是如果 相乘的两个因数⾮常⼤，语⾔提供的数据类型可能就会溢出。⼀种替代⽅案 就是，运算数以字符串的形式输⼊，然后模仿我们⼩学学习的乘法算术过程 计算出结果，并且也⽤字符串表⽰。</p>
<h2 id="题目"><a href="#题目" class="headerlink" title="题目"></a>题目</h2><p>给定两个以字符串形式表示的非负整数num1、num2，返回num1和num2的乘积，它们的乘积也表示为字符串的形式。</p>
<p>需要注意的是， num1 和 num2 可以⾮常⻓，所以不可以把他们直接转成 整型然后运算，唯⼀的思路就是模仿我们⼿算乘法。 </p>
<h2 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h2><p>计算 123 × 5 ，再计算 123 × 4 ，最后错⼀位相加。这个流程恐怕⼩学⽣ 都可以熟练完成，但是你是否能<strong>把这个运算过程进⼀步机械化</strong>，写成⼀套算 法指令让没有任何智商的计算机来执⾏呢？ </p>
<p>你看这个简单过程，其中涉及乘法进位，涉及错位相加，还涉及加法进位； ⽽且还有⼀些不易察觉的问题，⽐如说两位数乘以两位数，结果可能是四位 数，也可能是三位数，你怎么想出⼀个标准化的处理⽅式？这就是算法的魅 ⼒，如果没有计算机思维，简单的问题可能都没办法⾃动化处理。 </p>
<p>⾸先，我们这种⼿算⽅式还是太「⾼级」了，我们要再「低级」⼀点， 123 × 5 和 123 × 4 的过程还可以进⼀步分解，最后再相加： </p>
<p>现在 123 并不⼤，如果是个很⼤的数字的话，是⽆法直接计算乘积的。我 们可以⽤⼀个数组在底下接收相加结果： </p>
<p>整个计算过程⼤概是这样，<strong>有两个指针</strong> <strong>i</strong><em>，*<em>j</em></em> <strong>在</strong> <strong>num1</strong> <strong>和</strong> <strong>num2</strong> <strong>上游⾛，</strong> </p>
<p><strong>计算乘积，同时将乘积叠加到</strong> <strong>res</strong> <strong>的正确位置</strong>：  现在还有⼀个关键问题，如何将乘积叠加到 res 的正确位置，或者说，如 何通过 i，j 计算 res 的对应索引呢？ 其实，细⼼观察之后就发现， <strong>num1[i]</strong> <strong>和</strong> <strong>num2[j]</strong> <strong>的乘积对应的就是</strong> <strong>res[i+j]</strong> <strong>和</strong> <strong>res[i+j+1]</strong> <strong>这两个位置</strong>。 </p>
<p>明⽩了这⼀点，就可以⽤代码模仿出这个计算过程了：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="built_in">string</span> <span class="title">multiply</span><span class="params">(<span class="built_in">string</span> num1, <span class="built_in">string</span> num2)</span> </span>&#123; </span><br><span class="line">    <span class="keyword">int</span> m = num1.<span class="built_in">size</span>(), n = num2.<span class="built_in">size</span>(); </span><br><span class="line">    <span class="comment">// 结果最多为 m + n 位数 </span></span><br><span class="line">    <span class="function"><span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; <span class="title">res</span><span class="params">(m + n, <span class="number">0</span>)</span></span>; </span><br><span class="line">    <span class="comment">// 从个位数开始逐位相乘 </span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = m - <span class="number">1</span>; i &gt;= <span class="number">0</span>; i--) </span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> j = n - <span class="number">1</span>; j &gt;= <span class="number">0</span>; j--) &#123; </span><br><span class="line">            <span class="keyword">int</span> mul = (num1[i]-<span class="string">'0'</span>) * (num2[j]-<span class="string">'0'</span>); </span><br><span class="line">            <span class="comment">// 乘积在 res 对应的索引位置 </span></span><br><span class="line">            <span class="keyword">int</span> p1 = i + j, p2 = i + j + <span class="number">1</span>; </span><br><span class="line">            <span class="comment">// 叠加到 res 上 </span></span><br><span class="line">            <span class="keyword">int</span> sum = mul + res[p2]; </span><br><span class="line">            res[p2] = sum % <span class="number">10</span>; </span><br><span class="line">            res[p1] += sum / <span class="number">10</span>; </span><br><span class="line">        &#125; </span><br><span class="line">    <span class="comment">// 结果前缀可能存的 0（未使⽤的位） </span></span><br><span class="line">    <span class="keyword">int</span> i = <span class="number">0</span>; </span><br><span class="line">    <span class="keyword">while</span> (i &lt; res.<span class="built_in">size</span>() &amp;&amp; res[i] == <span class="number">0</span>) </span><br><span class="line">        i++; </span><br><span class="line">    <span class="comment">// 将计算结果转化成字符串 </span></span><br><span class="line">    <span class="built_in">string</span> str; </span><br><span class="line">    <span class="keyword">for</span> (; i &lt; res.<span class="built_in">size</span>(); i++) </span><br><span class="line">        str.push_back(<span class="string">'0'</span> + res[i]); </span><br><span class="line">    <span class="keyword">return</span> str.<span class="built_in">size</span>() == <span class="number">0</span> ? <span class="string">"0"</span> : str; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>总结⼀下</strong>，我们习以为常的⼀些思维⽅式，在计算机看来是⾮常难以做到 的。⽐如说我们习惯的算术流程并不复杂，但是如果让你再进⼀步，翻译成 代码逻辑，并不简单。算法需要将计算流程再简化，通过边算边叠加的⽅式 来得到结果。 </p>
<p>俗话教育我们，不要陷⼊思维定式，不要程序化，要发散思维，要创新。但 我觉得程序化并不是坏事，可以⼤幅提⾼效率，减⼩失误率。算法不就是⼀ 套程序化的思维吗，只有程序化才能让计算机帮助我们解决复杂问题呀！</p>
<h1 id="前缀和技巧"><a href="#前缀和技巧" class="headerlink" title="前缀和技巧"></a>前缀和技巧</h1><h2 id="题目："><a href="#题目：" class="headerlink" title="题目："></a>题目：</h2><p>算出⼀共有⼏个和为 k 的⼦数组。给定一个整数数组和一个整数k，你需要找到该数组中和为k的连续的子数组的个数。</p>
<p>输入：nums = [1,1,1], k = 2；输出：2，[1,1]与[1,1]为两种不同的情况。</p>
<h2 id="解法思路："><a href="#解法思路：" class="headerlink" title="解法思路："></a>解法思路：</h2><p>那我把所有⼦数组都穷举出来，算它们的和，看看谁的和等于 k 不就⾏了。 关键是，<strong>如何快速得到某个⼦数组的和呢</strong>，⽐如说给你⼀个数组 nums ，让 你实现⼀个接⼝ sum(i, j) ，这个接⼝要返回 nums[i..j] 的和，⽽且会被 多次调⽤，你怎么实现这个接⼝呢？ </p>
<p>因为接⼝要被多次调⽤，显然不能每次都去遍历 nums[i..j] ，有没有⼀种 快速的⽅法在 O(1) 时间内算出 nums[i..j] 呢？这就需要<strong>前缀和</strong>技巧了。</p>
<h3 id="什么是前缀和"><a href="#什么是前缀和" class="headerlink" title="什么是前缀和"></a>什么是前缀和</h3><p>前缀和的思路是这样的，对于⼀个给定的数组 nums ，我们额外开辟⼀个前 缀和数组进⾏预处理：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> n = nums.length; <span class="comment">// 前缀和数组</span></span><br><span class="line"><span class="keyword">int</span>[] preSum = <span class="keyword">new</span> <span class="keyword">int</span>[n + <span class="number">1</span>]; </span><br><span class="line">preSum[<span class="number">0</span>] = <span class="number">0</span>; </span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; n; i++) </span><br><span class="line">    preSum[i + <span class="number">1</span>] = preSum[i] + nums[i];</span><br></pre></td></tr></table></figure>

<p>这个前缀和数组 preSum 的含义也很好理解， preSum[i] 就是 nums[0..i- 1] 的和。那么如果我们想求 nums[i..j] 的和，只需要⼀步操作 </p>
<p>preSum[j+1]-preSum[i] 即可，⽽不需要重新去遍历数组了。 </p>
<p>回到这个⼦数组问题，我们想求有多少个⼦数组的和为 k，借助前缀和技巧 </p>
<p>很容易写出⼀个解法： </p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">subarraySum</span><span class="params">(<span class="keyword">int</span>[] nums, <span class="keyword">int</span> k)</span> </span>&#123; </span><br><span class="line">    <span class="keyword">int</span> n = nums.length; </span><br><span class="line">    <span class="comment">// 构造前缀和 </span></span><br><span class="line">    <span class="keyword">int</span>[] sum = <span class="keyword">new</span> <span class="keyword">int</span>[n + <span class="number">1</span>]; </span><br><span class="line">    sum[<span class="number">0</span>] = <span class="number">0</span>; </span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; n; i++) </span><br><span class="line">        sum[i + <span class="number">1</span>] = sum[i] + nums[i]; </span><br><span class="line">    <span class="keyword">int</span> ans = <span class="number">0</span>; <span class="comment">// 穷举所有⼦数组 </span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt;= n; i++) </span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; i; j++)</span><br><span class="line">            <span class="comment">// sum of nums[j..i-1] </span></span><br><span class="line">            <span class="keyword">if</span> (sum[i] - sum[j] == k) </span><br><span class="line">                ans++; </span><br><span class="line">    <span class="keyword">return</span> ans; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这个解法的时间复杂度 O(N^2) 空间复杂度 O(N) ，并不是最优的解法。不 过通过这个解法理解了前缀和数组的⼯作原理之后，可以使⽤⼀些巧妙的办 法把时间复杂度进⼀步降低</p>
<h3 id="优化解法"><a href="#优化解法" class="headerlink" title="优化解法"></a>优化解法</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//前⾯的解法有嵌套的 for 循环：</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt;= n; i++) </span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; i; j++) </span><br><span class="line">        <span class="keyword">if</span> (sum[i] - sum[j] == k) </span><br><span class="line">            ans++;</span><br><span class="line"><span class="comment">//第⼆层 for 循环在⼲嘛呢？翻译⼀下就是，在计算，有⼏个 j 能够使得 sum[i] 和 sum[j] 的差为 k。毎找到⼀个这样的 j ，就把结果加⼀。 我们可以把 if 语句⾥的条件判断移项，这样写：</span></span><br><span class="line"><span class="keyword">if</span> (sum[j] == sum[i] - k) </span><br><span class="line">    ans++;</span><br><span class="line"><span class="comment">//优化的思路是：我直接记录下有⼏个 sum[j] 和 sum[i] - k 相等，直接更 新结果，就避免了内层的 for 循环。</span></span><br><span class="line"><span class="comment">//我们可以⽤哈希表，在记录前缀和的同 时记录该前缀和出现的次数。</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">subarraySum</span><span class="params">(<span class="keyword">int</span>[] nums, <span class="keyword">int</span> k)</span> </span>&#123; </span><br><span class="line">    <span class="keyword">int</span> n = nums.length; <span class="comment">// map：前缀和 -&gt; 该前缀和出现的次数 </span></span><br><span class="line">    HashMap&lt;Integer, Integer&gt;;</span><br><span class="line">    preSum = <span class="keyword">new</span> HashMap&lt;&gt;(); </span><br><span class="line">    <span class="comment">// base case </span></span><br><span class="line">    preSum.<span class="built_in">put</span>(<span class="number">0</span>, <span class="number">1</span>); </span><br><span class="line">    </span><br><span class="line">    <span class="keyword">int</span> ans = <span class="number">0</span>, sum0_i = <span class="number">0</span>; </span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; n; i++) &#123; </span><br><span class="line">        sum0_i += nums[i]; </span><br><span class="line">        <span class="comment">// 这是我们想找的前缀和 nums[0..j] </span></span><br><span class="line">        <span class="keyword">int</span> sum0_j = sum0_i - k; </span><br><span class="line">        <span class="comment">// 如果前⾯有这个前缀和，则直接更新答案 </span></span><br><span class="line">        <span class="keyword">if</span> (preSum.containsKey(sum0_j)) </span><br><span class="line">            ans += preSum.<span class="built_in">get</span>(sum0_j); </span><br><span class="line">        <span class="comment">// 把前缀和 nums[0..i] 加⼊并记录出现次数 </span></span><br><span class="line">        preSum.<span class="built_in">put</span>(sum0_i, preSum.getOrDefault(sum0_i, <span class="number">0</span>) + <span class="number">1</span>); </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> ans; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这样，就把时间复杂度降到了 O(N) ，是最优解法了。</p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>前缀和不难，却很有⽤，主要⽤于处理数组区间的问题。 ⽐如说，让你统计班上同学考试成绩在不同分数段的百分⽐，也可以利⽤前 缀和技巧</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span>[] scores; <span class="comment">// 存储着所有同学的分数 </span></span><br><span class="line"><span class="comment">// 试卷满分 150 分 </span></span><br><span class="line"><span class="keyword">int</span>[] count = <span class="keyword">new</span> <span class="keyword">int</span>[<span class="number">150</span> + <span class="number">1</span>] <span class="comment">// 记录每个分数有⼏个同学 </span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> score : scores)</span><br><span class="line">        count[score]++;</span><br><span class="line"><span class="comment">// 构造前缀和 </span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt; count.length; i++) </span><br><span class="line">    count[i] = count[i] + count[i<span class="number">-1</span>];</span><br></pre></td></tr></table></figure>

<p>这样，给你任何⼀个分数段，你都能通过前缀和相减快速计算出这个分数段 \的⼈数，百分⽐也就很容易计算了。 </p>
<p>但是，稍微复杂⼀些的算法问题，不⽌考察简单的前缀和技巧。⽐如本⽂探 讨的这道题⽬，就需要借助前缀和的思路做进⼀步的优化，借助哈希表去除 不必要的嵌套循环。可⻅对题⽬的理解和细节的分析能⼒对于算法的优化是 ⾄关重要的。 </p>

      
    </div>
    
    
    
    
    <div>
      
    </div>
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2020/09/23/%E5%89%8D%E7%AB%AF/jQuery%E6%95%99%E7%A8%8B/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/author.jpg">
      <meta itemprop="name" content="Li Yudong">
      <meta itemprop="description" content="">
    </span>
    
    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Technological Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2020/09/23/%E5%89%8D%E7%AB%AF/jQuery%E6%95%99%E7%A8%8B/" class="post-title-link" itemprop="url">jQuery教程</a>
        </h2>
    
        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
    
              <time title="创建时间：2020-09-23 22:53:01" itemprop="dateCreated datePublished" datetime="2020-09-23T22:53:01+08:00">2020-09-23</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2021-11-03 16:25:40" itemprop="dateModified" datetime="2021-11-03T16:25:40+08:00">2021-11-03</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E5%89%8D%E7%AB%AF%E9%A1%B5%E9%9D%A2/" itemprop="url" rel="index"><span itemprop="name">前端页面</span></a>
                </span>
            </span>
    
          
    
        </div>
      </header>
    
    
    
    
    <div class="post-body" itemprop="articleBody">
    
      
          <h1 id="jQuery基础"><a href="#jQuery基础" class="headerlink" title="jQuery基础"></a>jQuery基础</h1><p>jQuery 是一个 JavaScript 库。jQuery 极大地简化了 JavaScript 编程。</p>
<p>jQuery库包含以下功能：</p>
<ul>
<li>HTML 元素选取</li>
<li>HTML 元素操作</li>
<li>CSS 操作</li>
<li>HTML 事件函数</li>
<li>JavaScript 特效和动画</li>
<li>HTML DOM 遍历和修改</li>
<li>AJAX</li>
<li>Utilities</li>
</ul>
<p><strong>提示：</strong> 除此之外，Jquery还提供了大量的插件。</p>
<h2 id="jQuery语法"><a href="#jQuery语法" class="headerlink" title="jQuery语法"></a>jQuery语法</h2><p>jQuery 语法是通过选取 HTML 元素，并对选取的元素执行某些操作。</p>
<p>基础语法： <strong>$(*selector*).*action*()</strong></p>
<ul>
<li>美元符号定义 jQuery</li>
<li>选择符（selector）”查询”和”查找” HTML 元素</li>
<li>jQuery 的 action() 执行对元素的操作</li>
</ul>
<p>实例:</p>
<ul>
<li>$(this).hide() - 隐藏当前元素</li>
<li>$(“p”).hide() - 隐藏所有 <p> 元素</li>
<li>$(“p.test”).hide() - 隐藏所有 class=”test” 的 <p> 元素</li>
<li>$(“#test”).hide() - 隐藏 id=”test” 的元素</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//在我们的实例中的所有 jQuery 函数位于一个 document ready 函数中：</span></span><br><span class="line">$(<span class="built_in">document</span>).ready(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line"> </span><br><span class="line">   <span class="comment">// 开始写 jQuery 代码...</span></span><br><span class="line"> </span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">/*这是为了防止文档在完全加载（就绪）之前运行 jQuery 代码，即在 DOM 加载完成后才可以对 DOM 进行操作。</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">如果在文档没有完全加载之前就运行函数，操作可能失败。下面是两个具体的例子：</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">试图隐藏一个不存在的元素</span></span><br><span class="line"><span class="comment">获得未完全加载的图像的大小*/</span></span><br></pre></td></tr></table></figure>

<h2 id="jQuery选择器"><a href="#jQuery选择器" class="headerlink" title="jQuery选择器"></a>jQuery选择器</h2><p>jQuery 选择器允许您对 HTML 元素组或单个元素进行操作。jQuery 选择器基于元素的 id、类、类型、属性、属性值等”查找”（或选择）HTML 元素。 它基于已经存在的 <a href="https://www.runoob.com/cssref/css-selectors.html" target="_blank" rel="noopener">CSS 选择器</a>，除此之外，它还有一些自定义的选择器。</p>
<p>jQuery 中所有选择器都以美元符号开头：$()。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//jQuery 元素选择器基于元素名选取元素,在页面中选取所有 &lt;p&gt; 元素。</span></span><br><span class="line"><span class="comment">//用户点击按钮后，所有 &lt;p&gt; 元素都隐藏。</span></span><br><span class="line">$(<span class="built_in">document</span>).ready(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  $(<span class="string">"button"</span>).click(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    $(<span class="string">"p"</span>).hide();</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">//jQuery #id 选择器通过 HTML 元素的 id 属性选取指定的元素。</span></span><br><span class="line"><span class="comment">//页面中元素的 id 应该是唯一的，所以您要在页面中选取唯一的元素需要通过 #id 选择器。</span></span><br><span class="line">$(<span class="built_in">document</span>).ready(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  $(<span class="string">"button"</span>).click(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    $(<span class="string">"#test"</span>).hide();</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">//jQuery 类选择器可以通过指定的 class 查找元素。</span></span><br><span class="line">$(<span class="built_in">document</span>).ready(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  $(<span class="string">"button"</span>).click(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    $(<span class="string">".test"</span>).hide();</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">//如果您的网站包含许多页面，并且您希望您的 jQuery 函数易于维护，那么请把您的 jQuery 函数放到独立的 .js 文件中。</span></span><br><span class="line"><span class="comment">//当我们在教程中演示 jQuery 时，会将函数直接添加到 &lt;head&gt; 部分中。不过，把它们放到一个单独的文件中会更好，就像这样（通过 src 属性来引用文件）：</span></span><br><span class="line">&lt;head&gt;</span><br><span class="line">&lt;script src=<span class="string">"http://cdn.static.runoob.com/libs/jquery/1.10.2/jquery.min.js"</span>&gt;</span><br><span class="line">&lt;<span class="regexp">/script&gt;</span></span><br><span class="line"><span class="regexp">&lt;script src="my_jquery_functions.js"&gt;&lt;/</span>script&gt;</span><br><span class="line">&lt;<span class="regexp">/head&gt;</span></span><br></pre></td></tr></table></figure>

<h2 id="jQuery事件"><a href="#jQuery事件" class="headerlink" title="jQuery事件"></a>jQuery事件</h2><p>在 jQuery 中，大多数 DOM 事件都有一个等效的 jQuery 方法。页面中指定一个点击事件：下一步是定义什么时间触发事件，可以通过一个事件函数来实现；</p>
<h3 id="常用的-jQuery-事件方法"><a href="#常用的-jQuery-事件方法" class="headerlink" title="常用的 jQuery 事件方法"></a>常用的 jQuery 事件方法</h3><h4 id="document-ready"><a href="#document-ready" class="headerlink" title="$(document).ready()"></a>$(document).ready()</h4><p>$(document).ready() 方法允许我们在文档完全加载完后执行函数。该事件方法在 <a href="https://www.runoob.com/jquery/jquery-syntax.html" target="_blank" rel="noopener">jQuery 语法</a> 章节中已经提到过。</p>
<h4 id="click"><a href="#click" class="headerlink" title="click()"></a>click()</h4><p>click() 方法是当按钮点击事件被触发时会调用一个函数。该函数在用户点击 HTML 元素时执行。</p>
<h4 id="dblclick"><a href="#dblclick" class="headerlink" title="dblclick()"></a>dblclick()</h4><p>当双击元素时，会发生 dblclick 事件。</p>
<p>dblclick() 方法触发 dblclick 事件，或规定当发生 dblclick 事件时运行的函数：</p>
<h4 id="mouseenter"><a href="#mouseenter" class="headerlink" title="mouseenter()"></a>mouseenter()</h4><p>当鼠标指针穿过元素时，会发生 mouseenter 事件。</p>
<p>mouseenter() 方法触发 mouseenter 事件，或规定当发生 mouseenter 事件时运行的函数：</p>
<h4 id="mouseleave"><a href="#mouseleave" class="headerlink" title="mouseleave()"></a>mouseleave()</h4><p>当鼠标指针离开元素时，会发生 mouseleave 事件。</p>
<p>mouseleave() 方法触发 mouseleave 事件，或规定当发生 mouseleave 事件时运行的函数：</p>
<h4 id="mousedown"><a href="#mousedown" class="headerlink" title="mousedown()"></a>mousedown()</h4><p>当鼠标指针移动到元素上方，并按下鼠标按键时，会发生 mousedown 事件。</p>
<p>mousedown() 方法触发 mousedown 事件，或规定当发生 mousedown 事件时运行的函数</p>
<h4 id="mouseup"><a href="#mouseup" class="headerlink" title="mouseup()"></a>mouseup()</h4><p>当在元素上松开鼠标按钮时，会发生 mouseup 事件。</p>
<p>mouseup() 方法触发 mouseup 事件，或规定当发生 mouseup 事件时运行的函数：</p>
<h4 id="hover"><a href="#hover" class="headerlink" title="hover()"></a>hover()</h4><p>hover()方法用于模拟光标悬停事件。</p>
<p>当鼠标移动到元素上时，会触发指定的第一个函数(mouseenter);当鼠标移出这个元素时，会触发指定的第二个函数(mouseleave)。</p>
<h4 id="focus"><a href="#focus" class="headerlink" title="focus()"></a>focus()</h4><p>当元素获得焦点时，发生 focus 事件。</p>
<p>当通过鼠标点击选中元素或通过 tab 键定位到元素时，该元素就会获得焦点。</p>
<p>focus() 方法触发 focus 事件，或规定当发生 focus 事件时运行的函数</p>
<h4 id="blur"><a href="#blur" class="headerlink" title="blur()"></a>blur()</h4><p>当元素失去焦点时，发生 blur 事件。</p>
<p>blur() 方法触发 blur 事件，或规定当发生 blur 事件时运行的函数：</p>
<h1 id="jQuery效果"><a href="#jQuery效果" class="headerlink" title="jQuery效果"></a>jQuery效果</h1><h2 id="隐藏与显示"><a href="#隐藏与显示" class="headerlink" title="隐藏与显示"></a>隐藏与显示</h2><p>通过 jQuery，您可以使用 hide() 和 show() 方法来隐藏和显示 HTML 元素：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">$(selector).hide(speed,callback);</span><br><span class="line">$(selector).show(speed,callback);</span><br><span class="line"><span class="comment">//可选的 speed 参数规定隐藏/显示的速度，可以取以下值："slow"、"fast" 或毫秒。</span></span><br><span class="line"><span class="comment">//可选的 callback 参数是隐藏或显示完成后所执行的函数名称。</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//通过 jQuery，您可以使用 toggle() 方法来切换 hide() 和 show() 方法。</span></span><br><span class="line"><span class="comment">//显示被隐藏的元素，并隐藏已显示的元素：</span></span><br><span class="line">$(<span class="string">"button"</span>).click(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  $(<span class="string">"p"</span>).toggle();</span><br><span class="line">&#125;);</span><br><span class="line">$(selector).toggle(speed,callback);</span><br><span class="line"><span class="comment">//可选的 speed 参数规定隐藏/显示的速度，可以取以下值："slow"、"fast" 或毫秒。可选的 callback 参数是隐藏或显示完成后所执行的函数名称。</span></span><br></pre></td></tr></table></figure>

<h2 id="淡入淡出"><a href="#淡入淡出" class="headerlink" title="淡入淡出"></a>淡入淡出</h2><p>通过 jQuery，您可以实现元素的淡入淡出效果。jQuery 拥有下面四种 fade 方法：</p>
<ul>
<li>fadeIn()</li>
<li>fadeOut()</li>
<li>fadeToggle()</li>
<li>fadeTo()</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//jQuery fadeIn() 用于淡入已隐藏的元素;</span></span><br><span class="line">$(selector).fadeIn(speed,callback);</span><br><span class="line">$(<span class="string">"button"</span>).click(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  $(<span class="string">"#div1"</span>).fadeIn();</span><br><span class="line">  $(<span class="string">"#div2"</span>).fadeIn(<span class="string">"slow"</span>);</span><br><span class="line">  $(<span class="string">"#div3"</span>).fadeIn(<span class="number">3000</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">//jQuery fadeOut() 方法用于淡出可见元素</span></span><br><span class="line">$(selector).fadeOut(speed,callback);</span><br><span class="line">$(<span class="string">"button"</span>).click(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  $(<span class="string">"#div1"</span>).fadeOut();</span><br><span class="line">  $(<span class="string">"#div2"</span>).fadeOut(<span class="string">"slow"</span>);</span><br><span class="line">  $(<span class="string">"#div3"</span>).fadeOut(<span class="number">3000</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">//jQuery fadeToggle() 方法可以在 fadeIn() 与 fadeOut() 方法之间进行切换。</span></span><br><span class="line"><span class="comment">//如果元素已淡出，则 fadeToggle() 会向元素添加淡入效果。如果元素已淡入，则 fadeToggle() 会向元素添加淡出效果。</span></span><br><span class="line">$(<span class="string">"button"</span>).click(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  $(<span class="string">"#div1"</span>).fadeToggle();</span><br><span class="line">  $(<span class="string">"#div2"</span>).fadeToggle(<span class="string">"slow"</span>);</span><br><span class="line">  $(<span class="string">"#div3"</span>).fadeToggle(<span class="number">3000</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">//jQuery fadeTo() 方法允许渐变为给定的不透明度（值介于 0 与 1 之间）。</span></span><br><span class="line">$(<span class="string">"button"</span>).click(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  $(<span class="string">"#div1"</span>).fadeTo(<span class="string">"slow"</span>,<span class="number">0.15</span>);</span><br><span class="line">  $(<span class="string">"#div2"</span>).fadeTo(<span class="string">"slow"</span>,<span class="number">0.4</span>);</span><br><span class="line">  $(<span class="string">"#div3"</span>).fadeTo(<span class="string">"slow"</span>,<span class="number">0.7</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<h2 id="滑动效果"><a href="#滑动效果" class="headerlink" title="滑动效果"></a>滑动效果</h2><p>通过 jQuery，您可以在元素上创建滑动效果。jQuery 拥有以下滑动方法：</p>
<ul>
<li>slideDown()</li>
<li>slideUp()</li>
<li>slideToggle()</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//jQuery slideDown() 方法用于向下滑动元素。</span></span><br><span class="line"><span class="comment">//jQuery slideUp() 方法用于向上滑动元素。</span></span><br><span class="line"><span class="comment">//jQuery slideToggle() 方法可以在 slideDown() 与 slideUp() 方法之间进行切换。</span></span><br><span class="line"><span class="comment">//如果元素向下滑动，则 slideToggle() 可向上滑动它们。如果元素向上滑动，则 slideToggle() 可向下滑动它们。</span></span><br></pre></td></tr></table></figure>

<h2 id="jQuery动画"><a href="#jQuery动画" class="headerlink" title="jQuery动画"></a>jQuery动画</h2><p>jQuery animate() 方法用于创建自定义动画。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">$(selector).animate(&#123;params&#125;,speed,callback);</span><br><span class="line"><span class="comment">/*必需的 params 参数定义形成动画的 CSS 属性。</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">可选的 speed 参数规定效果的时长。它可以取以下值："slow"、"fast" 或毫秒。</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">可选的 callback 参数是动画完成后所执行的函数名称。</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">下面的例子演示 animate() 方法的简单应用。它把 &lt;div&gt; 元素往右边移动了 250 像素：*/</span></span><br><span class="line">$(<span class="string">"button"</span>).click(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  $(<span class="string">"div"</span>).animate(&#123;<span class="attr">left</span>:<span class="string">'250px'</span>&#125;);</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">//默认情况下，所有的 HTML 元素有一个静态的位置，且是不可移动的。 如果需要改变为，我们需要将元素的 position 属性设置为 relative, fixed, 或 absolute!</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//同样生成动画的过程中可以同时使用多个属性</span></span><br><span class="line">$(<span class="string">"button"</span>).click(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  $(<span class="string">"div"</span>).animate(&#123;</span><br><span class="line">    left:<span class="string">'250px'</span>,</span><br><span class="line">    opacity:<span class="string">'0.5'</span>,</span><br><span class="line">    height:<span class="string">'150px'</span>,</span><br><span class="line">    width:<span class="string">'150px'</span></span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">//：当使用 animate() 时，必须使用 Camel 标记法书写所有的属性名，比如，必须使用 paddingLeft 而不是 padding-left，使用 marginRight 而不是 margin-right，等等。</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//也可以定义相对值（该值相对于元素的当前值）。需要在值的前面加上 += 或 -=：</span></span><br><span class="line"><span class="comment">//默认地，jQuery 提供针对动画的队列功能。</span></span><br><span class="line"><span class="comment">//这意味着如果您在彼此之后编写多个 animate() 调用，jQuery 会创建包含这些方法调用的"内部"队列。然后逐一运行这些 animate 调用。</span></span><br><span class="line">$(<span class="string">"button"</span>).click(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  <span class="keyword">var</span> div=$(<span class="string">"div"</span>);</span><br><span class="line">  div.animate(&#123;<span class="attr">left</span>:<span class="string">'100px'</span>&#125;,<span class="string">"slow"</span>);</span><br><span class="line">  div.animate(&#123;<span class="attr">fontSize</span>:<span class="string">'3em'</span>&#125;,<span class="string">"slow"</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">//</span></span><br></pre></td></tr></table></figure>

<p>jQuery stop() 方法用于在动画或效果完成前对它们进行停止。stop() 方法适用于所有 jQuery 效果函数，包括滑动、淡入淡出和自定义动画</p>
<p>$(<em>selector</em>).stop(<em>stopAll,goToEnd</em>)。</p>
<p>可选的 stopAll 参数规定是否应该清除动画队列。默认是 false，即仅停止活动的动画，允许任何排入队列的动画向后执行。可选的 goToEnd 参数规定是否立即完成当前动画。默认是 false。因此，默认地，stop() 会清除在被选元素上指定的当前动画。</p>
<p>Callback 函数在当前动画 100% 完成之后执行。</p>
<h2 id="jQuery链"><a href="#jQuery链" class="headerlink" title="jQuery链"></a>jQuery链</h2><p>通过 jQuery，可以把动作/方法链接在一起。Chaining 允许我们在一条语句中运行多个 jQuery 方法（在相同的元素上）</p>
<p>直到现在，我们都是一次写一条 jQuery 语句（一条接着另一条）。</p>
<p>不过，有一种名为链接（chaining）的技术，允许我们在相同的元素上运行多条 jQuery 命令，一条接着另一条。<strong>提示：</strong> 这样的话，浏览器就不必多次查找相同的元素。如需链接一个动作，您只需简单地把该动作追加到之前的动作上。下面的例子把 css()、slideUp() 和 slideDown() 链接在一起。”p1” 元素首先会变为红色，然后向上滑动，再然后向下滑动：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$(<span class="string">"#p1"</span>).css(<span class="string">"color"</span>,<span class="string">"red"</span>).slideUp(<span class="number">2000</span>).slideDown(<span class="number">2000</span>);</span><br><span class="line"><span class="comment">//如果需要，我们也可以添加多个方法调用。</span></span><br><span class="line"><span class="comment">//提示：当进行链接时，代码行会变得很长。不过，jQuery 语法不是很严格；您可以按照希望的格式来写，包含换行和缩进。如下书写也可以很好地运行：</span></span><br><span class="line">$(<span class="string">"#p1"</span>).css(<span class="string">"color"</span>,<span class="string">"red"</span>)</span><br><span class="line">  .slideUp(<span class="number">2000</span>)</span><br><span class="line">  .slideDown(<span class="number">2000</span>);</span><br><span class="line"><span class="comment">//jQuery 会抛掉多余的空格，并当成一行长代码来执行上面的代码行。</span></span><br></pre></td></tr></table></figure>

<h2 id="什么是链式调用"><a href="#什么是链式调用" class="headerlink" title="什么是链式调用"></a>什么是链式调用</h2><p>链式调用完方法后，return this返回当前调用方法的对象。普通的定义类方法，在多次调用一个对象的不同方法时有一个弊端，就是会多次重复使用一个对象变量，进行了多次查找，可以在原本的实现类中增添一行return this 从而能简单地实现链式调用。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//创建一个bird类</span></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">Bird</span>(<span class="params">name</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.name=name;</span><br><span class="line">        <span class="keyword">this</span>.run=<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">            <span class="built_in">document</span>.write(name+<span class="string">" "</span>+<span class="string">"start run;"</span>);</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">this</span>;<span class="comment">// return this返回当前调用方法的对象。</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">this</span>.stopRun=<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">            <span class="built_in">document</span>.write(name+<span class="string">" "</span>+<span class="string">"start run;"</span>);</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">this</span>.sing=<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">            <span class="built_in">document</span>.write(name+<span class="string">" "</span>+<span class="string">"start sing;"</span>);</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">this</span>.stopSing=<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">            <span class="built_in">document</span>.write(name+<span class="string">" "</span>+<span class="string">"start stopSing;"</span>);</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> bird=<span class="keyword">new</span> Bird(<span class="string">"测试"</span>);</span><br><span class="line">    bird.run().sing().stopSing().stopRun();</span><br><span class="line"><span class="comment">//结果为;测试 start run;测试 start sing;测试 start stopSing;测试 start run;</span></span><br></pre></td></tr></table></figure>

<h2 id="jq的链式调用"><a href="#jq的链式调用" class="headerlink" title="jq的链式调用"></a>jq的链式调用</h2><p>jq的链式调用其实就是比如我们在选择dom的时候，</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">$(<span class="string">'input[type="button"]'</span>)</span><br><span class="line">    .eq(<span class="number">0</span>).click(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        alert(<span class="string">'点击我!'</span>);</span><br><span class="line">&#125;).end().eq(<span class="number">1</span>)</span><br><span class="line">.click(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    $(<span class="string">'input[type="button"]:eq(0)'</span>).trigger(<span class="string">'click'</span>);</span><br><span class="line">&#125;).end().eq(<span class="number">2</span>)</span><br><span class="line">.toggle(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    $(<span class="string">'.aa'</span>).hide(<span class="string">'slow'</span>);</span><br><span class="line">&#125;, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    $(<span class="string">'.aa'</span>).show(<span class="string">'slow'</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>比如如上代码，先选择type类型为button的所有DOM，然后再选择第一个…</p>
<p>我们自然想到每次其实就是返回选择后的结果，在js里面有什么东西可以指代这个吗？<br>如果你想到this就对了。</p>
<p>q的方法都是挂在原型的，那么如果我们每次在内部方法返回this，也就是返回实例对象，那么我们就可以继续调用原型上的方法了，这样的就节省代码量，提高代码的效率，代码看起来更优雅。</p>
<p>但是也会出现一个问题：所有对象的方法返回的都是对象本身，也就是说没有返回值，所以这种方法不一定在任何环境下都适合。</p>
<h2 id="模仿jQuery的链式调用"><a href="#模仿jQuery的链式调用" class="headerlink" title="模仿jQuery的链式调用"></a>模仿jQuery的链式调用</h2><p>1、定义一个含参数的空对象；</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  <span class="comment">//下划线：表示私有变量的写法</span></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">_$</span>(<span class="params">els</span>) </span>&#123; &#125;;<span class="comment">//有参数的空函数对象</span></span><br><span class="line">&#125;)()<span class="comment">//程序启动的时候 里面的代码直接执行了</span></span><br></pre></td></tr></table></figure>

<p>2、准备方法：在_$上定义一个onrReady方法；</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">   <span class="comment">//第一步，下划线：表示私有变量的写法</span></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">_$</span>(<span class="params">els</span>) </span>&#123; &#125;;<span class="comment">//有参数的空对象</span></span><br><span class="line">    <span class="comment">//第二步，准备方法  在_$上定义一个onrReady方法</span></span><br><span class="line">    _$.onrReady=<span class="function"><span class="keyword">function</span> (<span class="params">fn</span>) </span>&#123;</span><br><span class="line">        <span class="comment">//按要求把对象（_$）注册到window对象上</span></span><br><span class="line">        <span class="comment">//对外开放的接口</span></span><br><span class="line">        <span class="built_in">window</span>.$=<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> _$(<span class="built_in">arguments</span>);</span><br><span class="line">            <span class="comment">//传递相应的方法调用参数  返回一可以使用function类原型上的方法的对象（$("")=_$(参数)）</span></span><br><span class="line">        &#125;</span><br><span class="line">        fn();</span><br><span class="line">    &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)()</span><br></pre></td></tr></table></figure>

<p>3、为了类能扩展函数，我们定义一个它的静态函数</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Function</span>.prototype.method=<span class="function"><span class="keyword">function</span> (<span class="params">name,fn</span>) </span>&#123;<span class="comment">//（函数名称，函数本身）</span></span><br><span class="line">     <span class="keyword">this</span>.prototype[name]=fn;</span><br><span class="line">     <span class="keyword">return</span> <span class="keyword">this</span>;<span class="comment">//链式调用关键</span></span><br><span class="line">&#125;;<span class="comment">//这个函数的意思：为function对象增加函数，会用链式调用，链式调用有两个参数name,和fn</span></span><br></pre></td></tr></table></figure>

<p>4、扩展类的相应方法，链式的对象增加jquery库提供的操作函数</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="comment">//下划线：表示私有变量的写法</span></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">_$</span>(<span class="params">els</span>) </span>&#123; &#125;;<span class="comment">//有参数的空对象</span></span><br><span class="line">    <span class="comment">//第二步，准备方法  在_$上定义一个onrReady方法</span></span><br><span class="line">    _$.onrReady=<span class="function"><span class="keyword">function</span> (<span class="params">fn</span>) </span>&#123;</span><br><span class="line">        <span class="comment">//按要求把对象（_$）注册到window对象上   </span></span><br><span class="line">        <span class="comment">//对外开放的接口</span></span><br><span class="line">        <span class="built_in">window</span>.$=<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> _$(<span class="built_in">arguments</span>);<span class="comment">//传递相应的方法调用参数  返回一可以使用function类原型上的方法的对象（$("")=_$(参数)）</span></span><br><span class="line">        &#125;</span><br><span class="line">        fn();</span><br><span class="line">    &#125;</span><br><span class="line">     <span class="comment">//第四步</span></span><br><span class="line">    <span class="comment">//扩展类的相应方法  链式的对象增加jquery库提供的操作函数  </span></span><br><span class="line">_$.method(<span class="string">"AddEvent"</span>,<span class="function"><span class="keyword">function</span> (<span class="params">type,fn</span>) </span>&#123;<span class="comment">//_$本身是一个function要继承原型链上的东西。</span></span><br><span class="line">    fn();</span><br><span class="line">&#125;).method(<span class="string">"getEvent"</span>,<span class="function"><span class="keyword">function</span> (<span class="params">fn,e</span>) </span>&#123;</span><br><span class="line">    fn();</span><br><span class="line">&#125;)</span><br><span class="line">&#125;)()</span><br></pre></td></tr></table></figure>

<p>5、使用，需要调用_$.onReady方法才可以返回对象使用从function类继承而来的原型上的方法</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="comment">// （1）下划线：表示私有变量的写法</span></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">_$</span>(<span class="params">els</span>) </span>&#123; &#125;;<span class="comment">//有参数的空对象</span></span><br><span class="line">    <span class="comment">//（2）准备方法  在_$上定义一个onrReady方法</span></span><br><span class="line">      <span class="comment">// window.$=_$;</span></span><br><span class="line">    _$.onrReady=<span class="function"><span class="keyword">function</span> (<span class="params">fn</span>) </span>&#123;</span><br><span class="line">        <span class="comment">//按要求把对象（_$）注册到window对象上   在任何地方都可以使用</span></span><br><span class="line">        <span class="comment">//对外开放的接口</span></span><br><span class="line">        <span class="built_in">window</span>.$=<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;<span class="comment">//window 上先注册一个全局变量 与外界产生关系</span></span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> _$(<span class="built_in">arguments</span>);<span class="comment">//传递相应的方法调用参数  返回一可以使用function类原型上的方法的对象（$("")=_$(参数)）</span></span><br><span class="line">        &#125;</span><br><span class="line">        fn();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//（4）扩展类的相应方法  链式的对象增加jquery库提供的操作函数</span></span><br><span class="line">_$.method(<span class="string">"AddEvent"</span>,<span class="function"><span class="keyword">function</span> (<span class="params">type,fn</span>) </span>&#123;<span class="comment">//_$本身是一个function要继承原型链上的东西。</span></span><br><span class="line">    fn();</span><br><span class="line">&#125;).method(<span class="string">"getEvent"</span>,<span class="function"><span class="keyword">function</span> (<span class="params">fn,e</span>) </span>&#123;</span><br><span class="line">    fn();</span><br><span class="line">&#125;);</span><br><span class="line">    <span class="comment">//第五步，开始使用 ，需要调用_$.onready方法才可以返回对象使用从function类继承而来的原型上的方法</span></span><br><span class="line">     _$.onrReady(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;<span class="comment">//$是绑定在Windows上的</span></span><br><span class="line">        $(<span class="string">""</span>).AddEvent(<span class="string">"click"</span>,<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">            alert(<span class="string">"click"</span>)</span><br><span class="line">        &#125;)</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;)()</span><br></pre></td></tr></table></figure>

<p><img src="/../../image/jQuery%E6%95%99%E7%A8%8B/%E9%93%BE%E5%BC%8F%E8%B0%83%E7%94%A8.png" alt="链式调用"></p>
<p>简单的加减法链式调用</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">test</span>(<span class="params">Number</span>)</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">isNaN</span>(<span class="built_in">Number</span>)?<span class="built_in">Number</span>:<span class="number">0</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">Add</span>(<span class="params">num</span>)</span>&#123;</span><br><span class="line">        test(num)</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span> + num</span><br><span class="line">    &#125;</span><br><span class="line">      <span class="function"><span class="keyword">function</span> <span class="title">jian</span>(<span class="params">num</span>)</span>&#123;</span><br><span class="line">           test(num)</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span> - num</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">Function</span>.prototype.Add = Add</span><br><span class="line">    <span class="built_in">Function</span>.prototype.jian = jian</span><br><span class="line"></span><br><span class="line">&#125;)()</span><br></pre></td></tr></table></figure>


      
    </div>
    
    
    
    
    <div>
      
    </div>
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2020/09/23/%E5%89%8D%E7%AB%AF/AJAX%E8%AF%B7%E6%B1%82/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/author.jpg">
      <meta itemprop="name" content="Li Yudong">
      <meta itemprop="description" content="">
    </span>
    
    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Technological Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2020/09/23/%E5%89%8D%E7%AB%AF/AJAX%E8%AF%B7%E6%B1%82/" class="post-title-link" itemprop="url">AJAX与Json</a>
        </h2>
    
        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
    
              <time title="创建时间：2020-09-23 19:36:18" itemprop="dateCreated datePublished" datetime="2020-09-23T19:36:18+08:00">2020-09-23</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2021-11-03 16:24:56" itemprop="dateModified" datetime="2021-11-03T16:24:56+08:00">2021-11-03</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E5%89%8D%E7%AB%AF%E9%A1%B5%E9%9D%A2/" itemprop="url" rel="index"><span itemprop="name">前端页面</span></a>
                </span>
            </span>
    
          
    
        </div>
      </header>
    
    
    
    
    <div class="post-body" itemprop="articleBody">
    
      
          <h1 id="AJAX"><a href="#AJAX" class="headerlink" title="AJAX"></a>AJAX</h1><h2 id="简介与实例"><a href="#简介与实例" class="headerlink" title="简介与实例"></a>简介与实例</h2><p>AJAX：即异步的JavaScript和XML，不是一种新的编程语言，而是一种使用现有标准的新方法。</p>
<p>AJAX 是一种用于创建快速动态网页的技术。通过在后台与服务器进行少量数据交换，AJAX 可以使网页实现异步更新。最大的优点在于不重新加载页面的情况下，可以与服务器交换数据并更新部分网页内容。</p>
<p>XMLHttpRequest 是 AJAX 的基础；有现代浏览器均支持 XMLHttpRequest 对象（IE5 和 IE6 使用 ActiveXObject）。XMLHttpRequest 用于在后台与服务器交换数据。这意味着可以在不重新加载整个网页的情况下，对网页的某部分进行更新。</p>
<p><em>variable</em>=new XMLHttpRequest();</p>
<h2 id="请求与响应"><a href="#请求与响应" class="headerlink" title="请求与响应"></a>请求与响应</h2><p>XMLHttpRequest 对象用于和服务器交换数据。如需将请求发送到服务器，我们使用 XMLHttpRequest 对象的 open() 和 send() 方法：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">xmlhttp.open(<span class="string">"GET"</span>,<span class="string">"ajax_info.txt"</span>,<span class="literal">true</span>);</span><br><span class="line">xmlhttp.send();</span><br></pre></td></tr></table></figure>

<p>open(<em>method</em>,<em>url</em>,<em>async</em>)规定请求的类型、URL 以及是否异步处理请求。</p>
<ul>
<li><em>method</em>：请求的类型；GET 或 POST</li>
<li><em>url</em>：文件在服务器上的位置</li>
<li><em>async</em>：true（异步）或 false（同步）</li>
</ul>
<p>send(<em>string</em>)将请求发送到服务器。</p>
<ul>
<li><em>string</em>：仅用于 POST 请求</li>
</ul>
<p>如需获得来自服务器的响应，请使用 XMLHttpRequest 对象的 responseText 或 responseXML 属性。</p>
<p>responseText：获得字符串形式的响应数据。</p>
<p>responseXML：获得 XML 形式的响应数据。</p>
<p>如果来自服务器的响应并非 XML，请使用 responseText 属性。responseText 属性返回字符串形式的响应，因此您可以这样使用：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">document</span>.getElementById(<span class="string">"myDiv"</span>).innerHTML=xmlhttp.responseText;</span><br></pre></td></tr></table></figure>



<p>如果来自服务器的响应是 XML，而且需要作为 XML 对象进行解析，请使用 responseXML 属性：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">xmlDoc=xmlhttp.responseXML;</span><br><span class="line">txt=<span class="string">""</span>;</span><br><span class="line">x=xmlDoc.getElementsByTagName(<span class="string">"ARTIST"</span>);</span><br><span class="line"><span class="keyword">for</span> (i=<span class="number">0</span>;i&lt;x.length;i++)</span><br><span class="line">&#123;</span><br><span class="line">    txt=txt + x[i].childNodes[<span class="number">0</span>].nodeValue + <span class="string">"&lt;br&gt;"</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">document</span>.getElementById(<span class="string">"myDiv"</span>).innerHTML=txt;</span><br></pre></td></tr></table></figure>

<h2 id="AJAX事件"><a href="#AJAX事件" class="headerlink" title="AJAX事件"></a>AJAX事件</h2><p>当请求被发送到服务器时，我们需要执行一些基于响应的任务。每当 readyState 改变时，就会触发 onreadystatechange 事件。readyState 属性存有 XMLHttpRequest 的状态信息。下面是 XMLHttpRequest 对象的三个重要的属性：</p>
<table>
<thead>
<tr>
<th>onreadystatechange</th>
<th>存储函数（或函数名），每当 readyState 属性改变时，就会调用该函数。</th>
</tr>
</thead>
<tbody><tr>
<td>readyState</td>
<td>存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。0: 请求未初始化1: 服务器连接已建立2: 请求已接收3: 请求处理中4: 请求已完成，且响应已就绪</td>
</tr>
<tr>
<td>status</td>
<td>200: “OK” 404: 未找到页面</td>
</tr>
</tbody></table>
<p>在 onreadystatechange 事件中，我们规定当服务器响应已做好被处理的准备时所执行的任务。当 readyState 等于 4 且状态为 200 时，表示响应已就绪：</p>
<h3 id="回调函数"><a href="#回调函数" class="headerlink" title="回调函数"></a>回调函数</h3><p>回调函数是一种以参数形式传递给另一个函数的函数。如果您的网站上存在多个 AJAX 任务，那么您应该为创建 XMLHttpRequest 对象编写一个<em>标准</em>的函数，并为每个 AJAX 任务调用该函数。该函数调用应该包含 URL 以及发生 onreadystatechange 事件时执行的任务（每次调用可能不尽相同）：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">myFunction</span>(<span class="params"></span>)</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    loadXMLDoc(<span class="string">"/try/ajax/ajax_info.txt"</span>,<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (xmlhttp.readyState==<span class="number">4</span> &amp;&amp; xmlhttp.status==<span class="number">200</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">document</span>.getElementById(<span class="string">"myDiv"</span>).innerHTML=xmlhttp.responseText;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>AJAX 用于创造动态性更强的应用程序</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">showHint</span>(<span class="params">str</span>)</span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">var</span> xmlhttp;</span><br><span class="line">    <span class="keyword">if</span> (str.length==<span class="number">0</span>)</span><br><span class="line">    &#123; </span><br><span class="line">        <span class="built_in">document</span>.getElementById(<span class="string">"txtHint"</span>).innerHTML=<span class="string">""</span>;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">window</span>.XMLHttpRequest)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码</span></span><br><span class="line">        xmlhttp=<span class="keyword">new</span> XMLHttpRequest();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// IE6, IE5 浏览器执行代码</span></span><br><span class="line">        xmlhttp=<span class="keyword">new</span> ActiveXObject(<span class="string">"Microsoft.XMLHTTP"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    xmlhttp.onreadystatechange=<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (xmlhttp.readyState==<span class="number">4</span> &amp;&amp; xmlhttp.status==<span class="number">200</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">document</span>.getElementById(<span class="string">"txtHint"</span>).innerHTML=xmlhttp.responseText;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    xmlhttp.open(<span class="string">"GET"</span>,<span class="string">"/try/ajax/gethint.php?q="</span>+str,<span class="literal">true</span>);</span><br><span class="line">    xmlhttp.send();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>源代码解析：</p>
<p>如果输入框为空 <strong>str.length==0</strong>，则该函数清空 <strong>txtHint</strong> 占位符的内容，并退出函数。</p>
<p>如果输入框不为空，<strong>showHint()</strong> 函数执行以下任务：</p>
<ul>
<li>创建 <strong>XMLHttpRequest</strong> 对象</li>
<li>当服务器响应就绪时执行函数</li>
<li>把请求发送到服务器上的文件</li>
<li>请注意我们向 URL 添加了一个参数 q （带有输入框的内容）</li>
</ul>
<p>由上面的 JavaScript 调用的服务器页面是 ASP 文件，名为 “gethint.asp”。</p>
<p>下面，我们创建了两个版本的服务器文件，一个用 ASP 编写，另一个用 PHP 编写。</p>
<h1 id="JSON"><a href="#JSON" class="headerlink" title="JSON"></a>JSON</h1><h2 id="语法与简介"><a href="#语法与简介" class="headerlink" title="语法与简介"></a>语法与简介</h2><p>JSON: <strong>J</strong>ava<strong>S</strong>cript <strong>O</strong>bject <strong>N</strong>otation(JavaScript 对象表示法)；JSON 是存储和交换文本信息的语法。类似 XML。JSON 比 XML 更小、更快，更易解析</p>
<ul>
<li>JSON 指的是 JavaScript 对象表示法（<strong>J</strong>ava<strong>S</strong>cript <strong>O</strong>bject <strong>N</strong>otation）</li>
<li>JSON 是轻量级的文本数据交换格式</li>
<li>JSON 独立于语言：JSON 使用 Javascript语法来描述数据对象，但是 JSON 仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。 目前非常多的动态（PHP，JSP，.NET）编程语言都支持JSON。</li>
<li>JSON 具有自我描述性，更易理解</li>
</ul>
<p>JSON 文本格式在语法上与创建 JavaScript 对象的代码相同。</p>
<p>由于这种相似性，无需解析器，JavaScript 程序能够使用内建的 eval() 函数，用 JSON 数据来生成原生的 JavaScript 对象。</p>
<h3 id="与XML对比"><a href="#与XML对比" class="headerlink" title="与XML对比"></a>与XML对比</h3><p>与 XML 相同之处：</p>
<ul>
<li>JSON 是纯文本</li>
<li>JSON 具有”自我描述性”（人类可读）</li>
<li>JSON 具有层级结构（值中存在值）</li>
<li>JSON 可通过 JavaScript 进行解析</li>
<li>JSON 数据可使用 AJAX 进行传输</li>
</ul>
<p>与 XML 不同之处：</p>
<ul>
<li>没有结束标签</li>
<li>更短</li>
<li>读写的速度更快</li>
<li>能够使用内建的 JavaScript eval() 方法进行解析</li>
<li>使用数组</li>
<li>不使用保留字</li>
</ul>
<p>为什么使用 JSON？</p>
<p>对于 AJAX 应用程序来说，JSON 比 XML 更快更易使用：</p>
<p>使用 XML：</p>
<ul>
<li>读取 XML 文档</li>
<li>使用 XML DOM 来循环遍历文档</li>
<li>读取值并存储在变量中</li>
</ul>
<p>使用 JSON：</p>
<ul>
<li>读取 JSON 字符串</li>
<li>用 eval() 处理 JSON 字符串</li>
</ul>
<h3 id="JSON语法"><a href="#JSON语法" class="headerlink" title="JSON语法"></a>JSON语法</h3><p>JSON 语法是 JavaScript 对象表示语法的子集。</p>
<ul>
<li>数据在名称/值对中</li>
<li>数据由逗号分隔</li>
<li>大括号 <strong>{}</strong> 保存对象</li>
<li>中括号 <strong>[]</strong> 保存数组，数组可以包含多个对象</li>
</ul>
<p>JSON 数据的书写格式是：key : value。名称/值对包括字段名称（在双引号中），后面写一个冒号，然后是值。</p>
<p>“name” : “菜鸟教程”。等价于这条 JavaScript 语句：name = “菜鸟教程”。</p>
<p>JSON 值可以是：</p>
<ul>
<li>数字（整数或浮点数）</li>
<li>字符串（在双引号中）</li>
<li>逻辑值（true 或 false）</li>
<li>数组（在中括号中）</li>
<li>对象（在大括号中）</li>
<li>null</li>
</ul>
<p>JSON 数组在中括号 <strong>[]</strong> 中书写：数组可包含多个对象：对象 <strong>sites</strong> 是包含三个对象的数组。每个对象代表一条关于某个网站（name、url）的记录。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="string">"sites"</span>: [</span><br><span class="line">        &#123; <span class="string">"name"</span>:<span class="string">"菜鸟教程"</span> , <span class="string">"url"</span>:<span class="string">"www.runoob.com"</span> &#125;, </span><br><span class="line">        &#123; <span class="string">"name"</span>:<span class="string">"google"</span> , <span class="string">"url"</span>:<span class="string">"www.google.com"</span> &#125;, </span><br><span class="line">        &#123; <span class="string">"name"</span>:<span class="string">"微博"</span> , <span class="string">"url"</span>:<span class="string">"www.weibo.com"</span> &#125;</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>因为 JSON 使用 JavaScript 语法，所以无需额外的软件就能处理 JavaScript 中的 JSON。通过 JavaScript，您可以创建一个对象数组，并像这样进行赋值：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> sites = [</span><br><span class="line">    &#123; <span class="string">"name"</span>:<span class="string">"runoob"</span> , <span class="string">"url"</span>:<span class="string">"www.runoob.com"</span> &#125;, </span><br><span class="line">    &#123; <span class="string">"name"</span>:<span class="string">"google"</span> , <span class="string">"url"</span>:<span class="string">"www.google.com"</span> &#125;, </span><br><span class="line">    &#123; <span class="string">"name"</span>:<span class="string">"微博"</span> , <span class="string">"url"</span>:<span class="string">"www.weibo.com"</span> &#125;</span><br><span class="line">];</span><br></pre></td></tr></table></figure>

<p>sites[0].name可以访问 JavaScript 对象数组中的第一项</p>
<ul>
<li>JSON 文件的文件类型是 <strong>.json</strong></li>
<li>JSON 文本的 MIME 类型是 <strong>application/json</strong></li>
</ul>
<h2 id="与XML相比"><a href="#与XML相比" class="headerlink" title="与XML相比"></a>与XML相比</h2><p>JSON 和 XML 都用于接收 web 服务端的数据。JSON 和 XML在写法上有所不同，如下所示：</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">"sites"</span>: [</span><br><span class="line">    &#123; <span class="attr">"name"</span>:<span class="string">"菜鸟教程"</span> , <span class="attr">"url"</span>:<span class="string">"www.runoob.com"</span> &#125;, </span><br><span class="line">    &#123; <span class="attr">"name"</span>:<span class="string">"google"</span> , <span class="attr">"url"</span>:<span class="string">"www.google.com"</span> &#125;, </span><br><span class="line">    &#123; <span class="attr">"name"</span>:<span class="string">"微博"</span> , <span class="attr">"url"</span>:<span class="string">"www.weibo.com"</span> &#125;</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">sites</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">site</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">name</span>&gt;</span>菜鸟教程<span class="tag">&lt;/<span class="name">name</span>&gt;</span> <span class="tag">&lt;<span class="name">url</span>&gt;</span>www.runoob.com<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">site</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">site</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">name</span>&gt;</span>google<span class="tag">&lt;/<span class="name">name</span>&gt;</span> <span class="tag">&lt;<span class="name">url</span>&gt;</span>www.google.com<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">site</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">site</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">name</span>&gt;</span>微博<span class="tag">&lt;/<span class="name">name</span>&gt;</span> <span class="tag">&lt;<span class="name">url</span>&gt;</span>www.weibo.com<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">site</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">sites</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>JSON 与 XML 的相同之处：</p>
<ul>
<li>JSON 和 XML 数据都是 “自我描述” ，都易于理解。</li>
<li>JSON 和 XML 数据都是有层次的结构</li>
<li>JSON 和 XML 数据可以被大多数编程语言使用</li>
<li></li>
</ul>
<p>JSON 与 XML 的不同之处：</p>
<ul>
<li>JSON 不需要结束标签</li>
<li>JSON 更加简短</li>
<li>JSON 读写速度更快</li>
<li>JSON 可以使用数组</li>
</ul>
<blockquote>
<p><strong>最大的不同是</strong>：XML 需要使用 XML 解析器来解析，JSON 可以使用标准的 JavaScript 函数来解析。</p>
<ul>
<li><a href="https://www.runoob.com/js/javascript-json-parse.html" target="_blank" rel="noopener">JSON.parse()</a>: 将一个 JSON 字符串转换为 JavaScript 对象。</li>
<li><a href="https://www.runoob.com/js/javascript-json-stringify.html" target="_blank" rel="noopener">JSON.stringify()</a>: 于将 JavaScript 值转换为 JSON 字符串。</li>
</ul>
</blockquote>
<h3 id="为什么-JSON-比-XML-更好？"><a href="#为什么-JSON-比-XML-更好？" class="headerlink" title="为什么 JSON 比 XML 更好？"></a>为什么 JSON 比 XML 更好？</h3><p>XML 比 JSON 更难解析。</p>
<p>JSON 可以直接使用现有的 JavaScript 对象解析。</p>
<p>针对 AJAX 应用，JSON 比 XML 数据加载更快，而且更简单：</p>
<p>使用 XML</p>
<ul>
<li>获取 XML 文档</li>
<li>使用 XML DOM 迭代循环文档</li>
<li>接数据解析出来复制给变量</li>
</ul>
<p>使用 JSON</p>
<ul>
<li>获取 JSON 字符串</li>
<li>JSON.Parse 解析 JSON 字符串</li>
</ul>
<h2 id="JSON对象与数组"><a href="#JSON对象与数组" class="headerlink" title="JSON对象与数组"></a>JSON对象与数组</h2><p>JSON 对象使用在大括号({})中书写。</p>
<p>对象可以包含多个 <strong>key/value（键/值）</strong>对。key 必须是字符串，value 可以是合法的 JSON 数据类型（字符串, 数字, 对象, 数组, 布尔值或 null）。key 和 value 中使用冒号(:)分割。每个 key/value 对使用逗号(,)分割。</p>
<p>你可以使用点号（.）来访问对象的值。你也可以使用中括号（[]）来访问对象的值：</p>
<h3 id="循环对象"><a href="#循环对象" class="headerlink" title="循环对象"></a>循环对象</h3><p>你可以使用 for-in 来循环对象的属性：在 for-in 循环对象的属性时，使用中括号（[]）来访问属性的值：</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">var myObj = &#123; "name":"runoob", "alexa":10000, "site":null &#125;;</span><br><span class="line">for (x in myObj) &#123;</span><br><span class="line">    document.getElementById("demo").innerHTML += myObj[x] + "&lt;br&gt;";</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="嵌套-JSON-对象"><a href="#嵌套-JSON-对象" class="headerlink" title="嵌套 JSON 对象"></a>嵌套 JSON 对象</h3><p>JSON 对象中可以包含另外一个 JSON 对象：你可以使用点号(.)或者中括号([])来访问嵌套的 JSON 对象。x = myObj.sites.site1; // 或者 x = myObj.sites[“site1”];</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">myObj = &#123;</span><br><span class="line">    "name":"runoob",</span><br><span class="line">    "alexa":10000,</span><br><span class="line">    "sites": &#123;</span><br><span class="line">        "site1":"www.runoob.com",</span><br><span class="line">        "site2":"m.runoob.com",</span><br><span class="line">        "site3":"c.runoob.com"</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>你可以使用点号(.)来修改 JSON 对象的值：你可以使用中括号([])来修改 JSON 对象的值：</p>
<p>我们可以使用 <strong>delete</strong> 关键字来删除 JSON 对象的属性：你可以使用中括号([])或者点号.来删除 JSON 对象的属性：</p>
<h2 id="数组作为-JSON-对象"><a href="#数组作为-JSON-对象" class="headerlink" title="数组作为 JSON 对象"></a>数组作为 JSON 对象</h2><p>JSON 数组在中括号中书写。JSON 中数组值必须是合法的 JSON 数据类型（字符串, 数字, 对象, 数组, 布尔值或 null）。</p>
<p>JavaScript 中，数组值可以是以上的 JSON 数据类型，也可以是 JavaScript 的表达式，包括函数，日期，及 <em>undefined</em>。</p>
<p>对象属性的值可以是一个数组：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line"><span class="string">"name"</span>:<span class="string">"网站"</span>,</span><br><span class="line"><span class="string">"num"</span>:<span class="number">3</span>,</span><br><span class="line"><span class="string">"sites"</span>:[ <span class="string">"Google"</span>, <span class="string">"Runoob"</span>, <span class="string">"Taobao"</span> ]</span><br><span class="line">&#125;</span><br><span class="line">x = myObj.sites[<span class="number">0</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">//可以使用for-in来访问数组</span></span><br><span class="line"><span class="keyword">for</span> (i <span class="keyword">in</span> myObj.sites) &#123;</span><br><span class="line">    x += myObj.sites[i] + <span class="string">"&lt;br&gt;"</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//也可以for循环</span></span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; myObj.sites.length; i++) &#123;</span><br><span class="line">    x += myObj.sites[i] + <span class="string">"&lt;br&gt;"</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//嵌套JSON对象中的数组,JSON对象中的数组可以包含另外一个数组，或者另外一个JSON对象；</span></span><br><span class="line">myObj = &#123;</span><br><span class="line">    <span class="string">"name"</span>:<span class="string">"网站"</span>,</span><br><span class="line">    <span class="string">"num"</span>:<span class="number">3</span>,</span><br><span class="line">    <span class="string">"sites"</span>: [</span><br><span class="line">        &#123; <span class="string">"name"</span>:<span class="string">"Google"</span>, <span class="string">"info"</span>:[ <span class="string">"Android"</span>, <span class="string">"Google 搜索"</span>, <span class="string">"Google 翻译"</span> ] &#125;,</span><br><span class="line">        &#123; <span class="string">"name"</span>:<span class="string">"Runoob"</span>, <span class="string">"info"</span>:[ <span class="string">"菜鸟教程"</span>, <span class="string">"菜鸟工具"</span>, <span class="string">"菜鸟微信"</span> ] &#125;,</span><br><span class="line">        &#123; <span class="string">"name"</span>:<span class="string">"Taobao"</span>, <span class="string">"info"</span>:[ <span class="string">"淘宝"</span>, <span class="string">"网购"</span> ] &#125;</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//可以使用for-in来循环访问每个数组:</span></span><br><span class="line"><span class="keyword">for</span> (i <span class="keyword">in</span> myObj.sites) &#123;</span><br><span class="line">    x += <span class="string">"&lt;h1&gt;"</span> + myObj.sites[i].name + <span class="string">"&lt;/h1&gt;"</span>;</span><br><span class="line">    <span class="keyword">for</span> (j <span class="keyword">in</span> myObj.sites[i].info) &#123;</span><br><span class="line">        x += myObj.sites[i].info[j] + <span class="string">"&lt;br&gt;"</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="JSON的方法与使用"><a href="#JSON的方法与使用" class="headerlink" title="JSON的方法与使用"></a>JSON的方法与使用</h2><h3 id="JSON-parse"><a href="#JSON-parse" class="headerlink" title="JSON.parse()"></a>JSON.parse()</h3><p>JSON 通常用于与服务端交换数据。在接收服务器数据时一般是字符串。我们可以使用 JSON.parse() 方法将数据转换为 JavaScript 对象。</p>
<p>JSON.parse(text[ , reviver])</p>
<ul>
<li>**text:**必需， 一个有效的 JSON 字符串。</li>
<li><strong>reviver:</strong> 可选，一个转换结果的函数， 将为对象的每个成员调用此函数。</li>
</ul>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//如果从服务器接收了以下的数据：</span></span><br><span class="line">&#123; <span class="attr">"name"</span>:<span class="string">"runoob"</span>, <span class="attr">"alexa"</span>:<span class="number">10000</span>, <span class="attr">"site"</span>:<span class="string">"www.runoob.com"</span> &#125;</span><br><span class="line"><span class="comment">//我们使用 JSON.parse() 方法处理以上数据，将其转换为 JavaScript 对象</span></span><br><span class="line">var obj = JSON.parse('&#123; "name":"runoob", "alexa":10000, "site":"www.runoob.com" &#125;');</span><br><span class="line"><span class="comment">//解析前要确保你的数据是标准的 JSON 格式，否则会解析出错解析完成后，我们就可以在网页上使用 JSON 数据了</span></span><br><span class="line">&lt;p id="demo"&gt;&lt;/p&gt;</span><br><span class="line"></span><br><span class="line">&lt;script&gt;</span><br><span class="line">var obj = JSON.parse('&#123; "name":"runoob", "alexa":10000, "site":"www.runoob.com" &#125;');</span><br><span class="line">document.getElementById("demo").innerHTML = obj.name + "：" + obj.site;</span><br><span class="line">&lt;/script&gt;</span><br><span class="line"></span><br><span class="line"><span class="comment">//我们可以使用AJAX从服务器请求JSON数据，并解析为JS对象</span></span><br><span class="line">var xmlhttp = new XMLHttpRequest();</span><br><span class="line">xmlhttp.onreadystatechange = function() &#123;</span><br><span class="line">    if (this.readyState == 4 &amp;&amp; this.status == 200) &#123;</span><br><span class="line">        myObj = JSON.parse(this.responseText);</span><br><span class="line">        document.getElementById("demo").innerHTML = myObj.name;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line">xmlhttp.open("GET", "/try/ajax/json_demo.txt", true);</span><br><span class="line">xmlhttp.send();</span><br><span class="line"><span class="comment">//如果服务端接受的是数组的JSON数据，则JSON.parse会将其转换为JS数组</span></span><br><span class="line">var xmlhttp = new XMLHttpRequest();</span><br><span class="line">xmlhttp.onreadystatechange = function() &#123;</span><br><span class="line">    if (this.readyState == 4 &amp;&amp; this.status == 200) &#123;</span><br><span class="line">        myArr = JSON.parse(this.responseText);</span><br><span class="line">        document.getElementById("demo").innerHTML = myArr[1];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line">xmlhttp.open("GET", "/try/ajax/json_demo_array.txt", true);</span><br><span class="line">xmlhttp.send();</span><br></pre></td></tr></table></figure>

<p>异常：解析数据，JSON不能存储Date对象，若需要存储Date对象，则需要先将其转换成字符串，之后再将字符串转换成Date对象。</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">var text = '&#123; "name":"Runoob", "initDate":"2013-12-14", "site":"www.runoob.com"&#125;';</span><br><span class="line">var obj = JSON.parse(text);</span><br><span class="line">obj.initDate = new Date(obj.initDate);</span><br><span class="line"> </span><br><span class="line">document.getElementById("demo").innerHTML = obj.name + "创建日期: " + obj.initDate;</span><br><span class="line"><span class="comment">//我们可以启用JSON.parse的第二个参数reviver，一个转换结果的函数，对象的每个成员调用此函数</span></span><br><span class="line">var text = '&#123; "name":"Runoob", "initDate":"2013-12-14", "site":"www.runoob.com"&#125;';</span><br><span class="line">var obj = JSON.parse(text, function (key, value) &#123;</span><br><span class="line">    if (key == "initDate") &#123;</span><br><span class="line">        return new Date(value);</span><br><span class="line">    &#125; else &#123;</span><br><span class="line">        return value;</span><br><span class="line">&#125;&#125;);</span><br><span class="line"> </span><br><span class="line">document.getElementById("demo").innerHTML = obj.name + "创建日期：" + obj.initDate;</span><br><span class="line"></span><br><span class="line"><span class="comment">//解析函数：JSON不允许包含函数，但可以将函数作为字符串存储，之后再将字符串转换为函数</span></span><br><span class="line">var text = '&#123; "name":"Runoob", "alexa":"function () &#123;return 10000;&#125;", "site":"www.runoob.com"&#125;';</span><br><span class="line">var obj = JSON.parse(text);</span><br><span class="line">obj.alexa = eval("(" + obj.alexa + ")");</span><br><span class="line"> </span><br><span class="line">document.getElementById("demo").innerHTML = obj.name + " Alexa 排名：" + obj.alexa();</span><br></pre></td></tr></table></figure>

<h3 id="JSON-stringify"><a href="#JSON-stringify" class="headerlink" title="JSON.stringify()"></a>JSON.stringify()</h3><p>JSON 通常用于与服务端交换数据。在向服务器发送数据时一般是字符串。我们可以使用 JSON.stringify() 方法将 JavaScript 对象转换为字符串。</p>
<p>JSON.stringify(value[, replacer[, space]])</p>
<ul>
<li><p>value:</p>
<p>必需， 要转换的 JavaScript 值（通常为对象或数组）。</p>
</li>
<li><p>replacer:</p>
<p>可选。用于转换结果的函数或数组。</p>
<p>如果 replacer 为函数，则 JSON.stringify 将调用该函数，并传入每个成员的键和值。使用返回值而不是原始值。如果此函数返回 undefined，则排除成员。根对象的键是一个空字符串：””。</p>
<p>如果 replacer 是一个数组，则仅转换该数组中具有键值的成员。成员的转换顺序与键在数组中的顺序一样。当 value 参数也为数组时，将忽略 replacer 数组。</p>
</li>
<li><p>space:</p>
<p>可选，文本添加缩进、空格和换行符，如果 space 是一个数字，则返回值文本在每个级别缩进指定数目的空格，如果 space 大于 10，则文本缩进 10 个空格。space 也可以使用非数字，如：\t。</p>
</li>
</ul>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">var obj = &#123; "name":"runoob", "alexa":10000, "site":"www.runoob.com"&#125;;</span><br><span class="line"><span class="comment">//使用JSON.stringify()方法处理以上数据，并将其转换为字符串</span></span><br><span class="line"></span><br><span class="line">var myJSON = JSON.stringify(obj);</span><br><span class="line">document.getElementById("demo").innerHTML = myJSON;</span><br><span class="line"></span><br><span class="line"><span class="comment">//同样也可以把JS数组转换成JSON字符串</span></span><br><span class="line">var arr = [ "Google", "Runoob", "Taobao", "Facebook" ];</span><br><span class="line">var myJSON = JSON.stringify(arr);</span><br><span class="line">document.getElementById("demo").innerHTML = myJSON;</span><br><span class="line"></span><br><span class="line"><span class="comment">//JSON.stringify() 会将所有日期转换为字符串。之后你可以再将字符串转换为 Date 对象</span></span><br><span class="line">var obj = &#123; "name":"Runoob", "initDate":new Date(), "site":"www.runoob.com"&#125;;</span><br><span class="line">var myJSON = JSON.stringify(obj);</span><br><span class="line">document.getElementById("demo").innerHTML = myJSON;</span><br><span class="line"><span class="comment">//JSON 不允许包含函数，JSON.stringify() 会删除 JavaScript 对象的函数，包括 key 和 value</span></span><br><span class="line">var obj = &#123; "name":"Runoob", "alexa":function () &#123;return 10000;&#125;, "site":"www.runoob.com"&#125;;</span><br><span class="line">var myJSON = JSON.stringify(obj);</span><br><span class="line"> </span><br><span class="line">document.getElementById("demo").innerHTML = myJSON;</span><br><span class="line"></span><br><span class="line"><span class="comment">//我们可以在执行 JSON.stringify() 函数前将函数转换为字符串来避免以上问题的发生：</span></span><br><span class="line">var obj = &#123; "name":"Runoob", "alexa":function () &#123;return 10000;&#125;, "site":"www.runoob.com"&#125;;</span><br><span class="line">obj.alexa = obj.alexa.toString();</span><br><span class="line">var myJSON = JSON.stringify(obj);</span><br><span class="line"> </span><br><span class="line">document.getElementById("demo").innerHTML = myJSON;</span><br><span class="line"><span class="comment">//因此不建议在JSON中使用函数</span></span><br></pre></td></tr></table></figure>

<h3 id="JSON使用与教程"><a href="#JSON使用与教程" class="headerlink" title="JSON使用与教程"></a>JSON使用与教程</h3><p>JSON 最常见的用法之一，是从 web 服务器上读取 JSON 数据（作为文件或作为 HttpRequest），将 JSON 数据转换为 JavaScript 对象，然后在网页中使用该数据。为了更简单地为您讲解，我们使用字符串作为输入进行演示（而不是文件）。</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//创建包含 JSON 语法的 JavaScript 字符串：</span></span><br><span class="line">var txt = '&#123; "sites" : [' +</span><br><span class="line">'&#123; "name":"菜鸟教程" , "url":"www.runoob.com" &#125;,' +</span><br><span class="line">'&#123; "name":"google" , "url":"www.google.com" &#125;,' +</span><br><span class="line">'&#123; "name":"微博" , "url":"www.weibo.com" &#125; ]&#125;';</span><br><span class="line"><span class="comment">//由于 JSON 语法是 JavaScript 语法的子集，JavaScript 函数 eval() 可用于将 JSON 文本转换为 JavaScript 对象。eval() 函数使用的是 JavaScript 编译器，可解析 JSON 文本，然后生成 JavaScript 对象。必须把文本包围在括号中，这样才能避免语法错误。</span></span><br><span class="line">var obj = eval ("(" + txt + ")");</span><br><span class="line"><span class="comment">//在网页中使用 JavaScript 对象</span></span><br><span class="line">var txt = '&#123; "sites" : [' +</span><br><span class="line">'&#123; "name":"菜鸟教程" , "url":"www.runoob.com" &#125;,' +</span><br><span class="line">'&#123; "name":"google" , "url":"www.google.com" &#125;,' +</span><br><span class="line">'&#123; "name":"微博" , "url":"www.weibo.com" &#125; ]&#125;';</span><br><span class="line"> </span><br><span class="line">var obj = eval ("(" + txt + ")");</span><br><span class="line"> </span><br><span class="line">document.getElementById("name").innerHTML=obj.sites[0].name </span><br><span class="line">document.getElementById("url").innerHTML=obj.sites[0].url</span><br><span class="line"></span><br><span class="line"><span class="comment">//JSON解析器</span></span><br><span class="line"> eval() 函数可编译并执行任何 JavaScript 代码。这隐藏了一个潜在的安全问题。</span><br><span class="line">使用 JSON 解析器将 JSON 转换为 JavaScript 对象是更安全的做法。JSON 解析器只能识别 JSON 文本，而不会编译脚本。</span><br><span class="line">在浏览器中，这提供了原生的 JSON 支持，而且 JSON 解析器的速度更快。</span><br><span class="line">较新的浏览器和最新的 ECMAScript (JavaScript) 标准中均包含了原生的对 JSON 的支持。</span><br></pre></td></tr></table></figure>

<h2 id="JSONP"><a href="#JSONP" class="headerlink" title="JSONP"></a>JSONP</h2><p>Jsonp(JSON with Padding) 是 json 的一种”使用模式”，可以让网页从别的域名（网站）那获取资料，即跨域读取数据。为什么我们从不同的域（网站）访问数据需要一个特殊的技术( JSONP )呢？这是因为同源策略。同源策略，它是由 Netscape 提出的一个著名的安全策略，现在所有支持 JavaScript 的浏览器都会使用这个策略。</p>

      
    </div>
    
    
    
    
    <div>
      
    </div>
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2020/09/16/%E7%AE%97%E6%B3%95/%E7%AE%97%E6%B3%95%E6%A1%86%E6%9E%B6/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/author.jpg">
      <meta itemprop="name" content="Li Yudong">
      <meta itemprop="description" content="">
    </span>
    
    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Technological Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2020/09/16/%E7%AE%97%E6%B3%95/%E7%AE%97%E6%B3%95%E6%A1%86%E6%9E%B6/" class="post-title-link" itemprop="url">动态规划与回溯算法框架</a>
        </h2>
    
        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
    
              <time title="创建时间：2020-09-16 23:04:18" itemprop="dateCreated datePublished" datetime="2020-09-16T23:04:18+08:00">2020-09-16</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2021-11-03 16:28:42" itemprop="dateModified" datetime="2021-11-03T16:28:42+08:00">2021-11-03</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E7%AE%97%E6%B3%95/" itemprop="url" rel="index"><span itemprop="name">算法</span></a>
                </span>
            </span>
    
          
    
        </div>
      </header>
    
    
    
    
    <div class="post-body" itemprop="articleBody">
    
      
          <h1 id="动态规划框架"><a href="#动态规划框架" class="headerlink" title="动态规划框架"></a>动态规划框架</h1><p>动态规划问题的一般形式是求最值；求解动态规划的核心问题是穷举，肯定要把所有可行的答案穷举出来然后在其中找最值；且一般具有重叠子问题，需要备忘录或者DP table来优化。</p>
<p>关键：状态转移方程，<strong>明确[状态]-&gt;定义DP数组、函数的含义-&gt;明确[选择]-&gt;明确basecase</strong>。</p>
<p>带备忘录的递归，有时候耗时的原因是重复计算，那么可以造备忘录，每次算出某子问题后不急着返回，先记到备忘录中；每次遇到一个子问题时，先去备忘录查一查，如果发现已经解决过，就直接拿出来用。</p>
<p>一般使用一个数组充当备忘录，当然也可以使用hash表，思想是一样的。</p>
<p>斐波那契数列问题：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> fit = <span class="function">(<span class="params">N</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (N &lt; <span class="number">1</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">var</span> memo = <span class="keyword">new</span> <span class="built_in">Array</span>(N + <span class="number">1</span>);</span><br><span class="line">    memo[<span class="number">1</span>] = memo[<span class="number">2</span>] = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">3</span>; i &lt;= N; i++)</span><br><span class="line">        memo[i] = memo[i<span class="number">-1</span>] + memo[i - <span class="number">2</span>];</span><br><span class="line">    <span class="keyword">return</span> memo[N];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>带备忘录的递归解法和迭代的动态规划解法其实一样，递归自顶向下，迭代递归自底向上；</p>
<p>动态规划的另一个重要特性：最优子结构；凑零钱问题：给你k种面值的硬币，面值分别为C1、C2、…Ck；再给一个总金额amount，问最少需要多少硬币凑出，如果不能凑出则返回-1。</p>
<p>要符合最优子结构，子问题之间必须相互独立，状态转移方程的步骤</p>
<p>1、确定状态，也就是原问题和子问题变化的变量，唯一状态为目标金额amount；</p>
<p>2、确定dp函数的定义：当前目标金额为n，则至少需要dp(n)个硬币；</p>
<p>3、确定选择并择优：对于每个状态，可以做出选择改变当前状态；具体至当前，无论目标金额多少，选择就是从面额列表coins种选择一个硬币，然后目标金额就会减少。</p>
<p>4、最后明确base case，显然目标金额为0时，所需硬币数量为0；当目标金额小于0时，无解，返回-1。</p>
<p>一般这种题需要使用备忘录来消除子问题，减少其冗余从而节省时间。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> coinChange = (coins, amount)&#123;</span><br><span class="line">    <span class="keyword">let</span> memo = [];</span><br><span class="line">    <span class="keyword">const</span> dp = <span class="function">(<span class="params">n</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (memo[n] != <span class="literal">null</span>) <span class="keyword">return</span> memo[n];</span><br><span class="line">        <span class="comment">//先查备忘录，避免重复计算</span></span><br><span class="line">        <span class="keyword">if</span> (n == <span class="number">0</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">if</span> (n &lt; <span class="number">0</span>) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">        res = MAX_VALUE;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; coins.length; i++)&#123;</span><br><span class="line">            <span class="keyword">let</span> subproblem == dp(n - coins[i]);</span><br><span class="line">            <span class="keyword">if</span> (subproblem == <span class="number">-1</span>) <span class="keyword">continue</span>;</span><br><span class="line">            res = min(res, <span class="number">1</span> + subproblem);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (res != MAX_VALUE)&#123;</span><br><span class="line">            memo[n] = res;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> memo[n] = <span class="number">-1</span>;</span><br><span class="line">        <span class="keyword">return</span> memo[n];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> dp(amount);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>除了这种自顶向下的带备忘录递归，自底向上的dp数组迭代也一样可以消除子问题：dp[i] = x，表示当目标金额为i时，至少需要x枚硬币。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> coinChange = <span class="function">(<span class="params">coins, amount</span>) =&gt;</span>&#123;</span><br><span class="line">    <span class="keyword">let</span> dp = <span class="keyword">new</span> <span class="built_in">Array</span>(amount + <span class="number">1</span>);</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; amount + <span class="number">1</span>; i++)&#123;</span><br><span class="line">        dp[i] = amount + <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//先初始化dp数组，长度与初始值</span></span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; dp.length; i++)&#123;</span><br><span class="line">        <span class="comment">//内层for在求所有子问题+1的最小值</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> j = <span class="number">0</span>; j &lt; coins.length; j++)&#123;</span><br><span class="line">            <span class="comment">//子问题无解则跳过</span></span><br><span class="line">            <span class="keyword">if</span> (i - coins[j] &lt; <span class="number">0</span>) <span class="keyword">continue</span>;</span><br><span class="line">            dp[i] = min(dp[i], <span class="number">1</span> + dp[i - coin]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (dp[amount] == amount + <span class="number">1</span>) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">return</span> dp[amount];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="动态规划之背包问题"><a href="#动态规划之背包问题" class="headerlink" title="动态规划之背包问题"></a>动态规划之背包问题</h2><p>给你一个则装载重量为W的背包和N个物品，每个物品有重量和价值两个属性，其中第i个物品重量为wt[i]，价值为val[i]，求使用该背包装物品的最大价值。</p>
<p>典型动态规划：物品不可分割，要么装进包里，要么不装，不可能切分。解决这种问题没有排序的巧妙方法，只能穷举所有可能，根据动态规划套路走流程：</p>
<p>1、明确状态与选择；状态：背包容量与可选择的物品；选择：装进背包或不装进背包。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">for 状态1 in 状态1的所有取值：</span><br><span class="line">	for 状态2 in 状态2的所有取值：</span><br><span class="line">		for ....</span><br><span class="line">			dp[状态1][状态2][...] &#x3D; 择优(选择1， 选择2， ...)</span><br></pre></td></tr></table></figure>

<p>2、明确DP数组：描述当前问题局面，需要使用DP数组把状态表示出来，而状态有2个，因此为二维数组dp[i] [w] :对于前i个物品，当前背包的容量为w，这种情况下可以装的最大价值为dp[i] [w]。且base case 就是dp[0] [..] = dp[..] [0] = 0。</p>
<p>3、根据选择，思考状态转移的逻辑：即把物品i装不装进背包的代码逻辑怎么体现。如果不装：dp[i] [w] = dp[i-1] [w]，即继承以前的结果；如果装：dp[i] [w] = dp[i-1] [w - wt[i-1]] + val[i-1]，在装第i个的前提下该如何最大价值，应寻求剩余重量w - wt[i-1]限制下的最大重量，再加上确定的第i个物品价值。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> knapsack = <span class="function">(<span class="params">W, N, wt, val</span>) =&gt;</span>&#123;</span><br><span class="line">    <span class="keyword">let</span> dp = <span class="keyword">new</span> <span class="built_in">Array</span>();</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; N + <span class="number">1</span>; i++)&#123;</span><br><span class="line">        dp[i] = <span class="keyword">new</span> <span class="built_in">Array</span>();</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> j = <span class="number">0</span>; j &lt; W + <span class="number">1</span>; j++)&#123;</span><br><span class="line">            dp[i][j] = <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;<span class="comment">//先定义二维数组，并均初始化为0</span></span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">1</span>; i &lt;= N; i++)&#123;</span><br><span class="line">        <span class="keyword">for</span> (w = <span class="number">1</span>; w &lt;= W; W++)&#123;</span><br><span class="line">            <span class="keyword">if</span> (w - wt[i - <span class="number">1</span>] &lt; <span class="number">0</span>)&#123;</span><br><span class="line">                <span class="comment">//当前背包容量放不下时，只能选择不装入背包</span></span><br><span class="line">                dp[i][w] = dp[i - <span class="number">1</span>][w];</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">//装入或不装入背包的择优</span></span><br><span class="line">                dp[i][w] = max(dp[i - <span class="number">1</span>][w - wt[i - <span class="number">1</span>]] + val[i - <span class="number">1</span>], dp[i - <span class="number">1</span>][w]);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> dp[N][W];</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//使用动态规划遍历时，同时择优并用DP表来存储已经计算完的数据。</span></span><br></pre></td></tr></table></figure>

<h2 id="动态规划之子集背包分割"><a href="#动态规划之子集背包分割" class="headerlink" title="动态规划之子集背包分割"></a>动态规划之子集背包分割</h2><p>怎么将⼆维动态规划压缩成⼀维动态规划吗？这就是状态压缩，很容易的，</p>
<p>题目：给定一个只包含正整数的非空数组，是否可以将该数组分割成两个子集，使两个子集的元素和相等：</p>
<p>对于这个问题，看起来和背包没有任何关系，为什么说它是背包问题呢？ ⾸先回忆⼀下背包问题⼤致的描述是什么： 给你⼀个可装载重量为 W 的背包和 N 个物品，每个物品有重量和价值两 个属性。其中第 i 个物品的重量为 wt[i] ，价值为 val[i] ，现在让你⽤ 这个背包装物品，最多能装的价值是多少？</p>
<p> 那么对于这个问题，我们可以先对集合求和，得出 sum ，把问题转化为背 包问题：<strong>给⼀个可装载重量为</strong> <strong>sum / 2</strong> <strong>的背包和</strong> <strong>N</strong> <strong>个物品，每个物品的重量为</strong> <strong>nums[i]</strong> <strong>。现在让你装物品，是否存在⼀种装法，能够恰好将背包装满</strong>？ </p>
<p>你看，这就是背包问题的模型，甚⾄⽐我们之前的经典背包问题还要简单⼀ 些，<strong>下⾯我们就直接转换成背包问题</strong>，开始套前⽂讲过的背包问题框架即就可。</p>
<p>1、明确状态与选择；状态：背包容量、可选择的物品；选择：是否装进背包</p>
<p>2、dp数组定义：dp[ i ] [ j ]= x 表⽰，对于前 i 个物品，当前背包的容量为 j 时，若 x 为 true ，则说明可以恰好将背包装满，若 x 为 false ，则说明不能恰 好将背包装满。 </p>
<p>3、根据选择来思考状态转移的逻辑，恰好把背包装满的条件，其实刚好又取决于其上一个状态；换句话说，如果 j - nums[i-1] 的重量可以被恰好装满，那么只要把第 i 个物品装进去，也可恰好装满 j 的重量；否则的话，重量 j 肯定是装不满的。 </p>
<p>4、basecase：想求的最终答案是dp[N] [sum/2]，因此初始条件为dp[…] [0] = true和dp[0] […] = false；</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> canPartition = <span class="function">(<span class="params">nums</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> sum = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; nums.length; i++)&#123;</span><br><span class="line">        sum += nums[i];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (sum % <span class="number">2</span> != <span class="number">0</span>) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    <span class="keyword">let</span> len = nums.length;</span><br><span class="line">    sum = sum / <span class="number">2</span>;</span><br><span class="line">    <span class="keyword">let</span> dp = <span class="keyword">new</span> <span class="built_in">Array</span>();</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//根据初始条件来初始化</span></span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt;= len + <span class="number">1</span>; i++) &#123;</span><br><span class="line">        dp[i] = <span class="keyword">new</span> <span class="built_in">Array</span>();</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> j = <span class="number">0</span>; j &lt;= sum + <span class="number">1</span>; j++)&#123;</span><br><span class="line">            dp[i][j] = <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt;= n; i++)&#123;</span><br><span class="line">        dp[i] [<span class="number">0</span>] = <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt;= n; i++)&#123;</span><br><span class="line">        <span class="keyword">for</span> (j = <span class="number">0</span>; j &lt;= sum; j++)&#123;</span><br><span class="line">            <span class="keyword">if</span> (j - nums[i - <span class="number">1</span>] &lt; <span class="number">0</span>)&#123;</span><br><span class="line">                <span class="comment">//背包不足，不能装入第i个物品</span></span><br><span class="line">                dp[i] [j] = dp[i - <span class="number">1</span>] [j];</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">//装入或不装入的选择</span></span><br><span class="line">                dp[i][j] = dp[i - <span class="number">1</span>] [j] | dp[i - <span class="number">1</span>] [j - nums[i - <span class="number">1</span>]];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> dp[n] [sum];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="动态规划之零钱兑换（完全背包问题）"><a href="#动态规划之零钱兑换（完全背包问题）" class="headerlink" title="动态规划之零钱兑换（完全背包问题）"></a>动态规划之零钱兑换（完全背包问题）</h2><p>题目：给定不同面额的硬币和一个总金额，假设每种硬币有无数个，写出函数来计算能凑成总金额的硬币总和数。</p>
<p>有⼀个背包，最⼤容量为 amount ，有⼀系列物品 coins ，每个物品的重量为 coins[i] ，<strong>每个物品的数量⽆限</strong>。请问有多少种⽅法，能够把背包恰 好装满？ </p>
<p>这个问题和我们前⾯讲过的两个背包问题，有⼀个最⼤的区别就是，每个物 品的数量是⽆限的，这也就是传说中的「<strong>完全背包问题</strong>」，没啥⾼⼤上的， ⽆⾮就是状态转移⽅程有⼀点变化⽽已。</p>
<p>1、状态与选择；状态：背包的容量，可选择的物品；选择：是否装进背包；</p>
<p>2、dp数组：若只使用前i个物品，当背包容量为j时，有dp[i] [j]种方法可以装满背包；</p>
<p>3、base case为dp[0] [..] = 0;dp[..] [0] = 1;不使用任何面值则无法完成，背包容量为0则仅有一种。</p>
<p>4、状态转移方程的逻辑：如果不把第i个物品装进背包，则凑出面额j的方法数dp[i] [j]等于dp[i - 1] [j]，继承之前的结果；如果你把第i个物品装进了背包，则说明使用了coins[i - 1] 这个面额，那么dp[i] [j] = dp [i] [j-coins[i - 1]]</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> change = <span class="function">(<span class="params">amount, coins</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> len = coins.length;</span><br><span class="line">    <span class="keyword">let</span> dp = <span class="keyword">new</span> <span class="built_in">Array</span>();</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//根据初始条件来初始化</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt;= len + <span class="number">1</span>; i++) &#123;</span><br><span class="line">        dp[i] = <span class="keyword">new</span> <span class="built_in">Array</span>();</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> j = <span class="number">0</span>; j &lt;= sum + <span class="number">1</span>; j++)&#123;</span><br><span class="line">            dp[i][j] = <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt;= n; i++)&#123;</span><br><span class="line">        dp[i] [<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">1</span>; i &lt;=n; i++)&#123;</span><br><span class="line">        <span class="keyword">for</span> (j = <span class="number">1</span>; j &lt;= amount; j++)&#123;</span><br><span class="line">            <span class="keyword">if</span> (j - coins[i - <span class="number">1</span>] &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">                dp[i][j] = dp[i - <span class="number">1</span>][j] + dp[i][j - coins[i - <span class="number">1</span>]];</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                dp[i][j] = dp[i - <span class="number">1</span>][j];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> dp[n][amount];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="动态规划最长递增子序列"><a href="#动态规划最长递增子序列" class="headerlink" title="动态规划最长递增子序列"></a>动态规划最长递增子序列</h2><p>动态规划的通用技巧：数学归纳思想；最长递增子序列LIS；子串与子序列名词的区别：字串一定是连续的，而子序列不一定是连续的。</p>
<p>1、明确状态与选择：状态，序列nums的所有字母；选择：更新子序列还是</p>
<p>2、用dp数组来描述状态，dp[i]表示以nums[i]这个数结尾的最长递增子序列的长度；后续的动态规划子序列解题模板总结了常见的套路:</p>
<p>3、根据选择思考动态转移方程的逻辑：已知dp[0..n-1]，根据nums[n]的值，便能够判断如何更新，找到结尾比nums[n]小的子序列，然后把nums[n]接到最后，形成新子序列，其长度为原子序列+1；且由于未知之前子序列的大小关系，需要使用遍历将前面的dp值全部遍历一遍；</p>
<p>4、base case: dp[i]初始值为1，因为以nums[i]结尾的LIS最少要包含它自己。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> lengthOfLIS = <span class="function">(<span class="params">nums</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> dp = <span class="keyword">new</span> <span class="built_in">Array</span>();</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; nums.length; i++)&#123;</span><br><span class="line">        dp[i] = <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; nums.length; i++)&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> j = <span class="number">0</span>; j &lt; i; j++)&#123;</span><br><span class="line">            <span class="keyword">if</span> (nums[i] &gt; nums[j])&#123;</span><br><span class="line">                dp[i] = max.(dp[i], dp[j] + <span class="number">1</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">let</span> res = <span class="number">0</span>;</span><br><span class="line">    <span class="comment">//将每个以nums[i]结尾的LIS长度存到dp之后，遍历一遍找出最大的那个</span></span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; dp.length; i++)&#123;</span><br><span class="line">        res = max.(res, dp[i]);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="动态规划：最长公共子序列LCS"><a href="#动态规划：最长公共子序列LCS" class="headerlink" title="动态规划：最长公共子序列LCS"></a>动态规划：最长公共子序列LCS</h2><p>LCS是典型的二维动态规划，目的是求两个字符串的LCS长度，子序列问题基本都用动态规划来实现，穷举加剪枝。</p>
<p>1、明确状态与选择：状态，两个字符串；选择，是否添加该元素作为最长公共子序列。</p>
<p>2、确定dp数组的含义： 两字符串数组的通用套路，dp[i] [j]代表s1[1…i]和s2[1…j]的LCS长度。</p>
<p>3、找状态转移方程：其实字符串问题的状态转移套路都差不多，都是判断字符在还是不在的问题，只有2种选择；如果某字符在LCS中，则其肯定同时存在于s1和s2中。</p>
<p>思路为：用2个指针从后往前遍历s1、s2，若相等则该字符一定在lcs中，否则这两个字符至少有一个不在lcs中，需要丢弃一个；</p>
<p>4、base case：均初始化为0，最短可以没有公共元素。</p>
<p>暴力的递归写法如下：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> longestCommonSubsequence = <span class="function">(<span class="params">str1, str2</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> dp = <span class="function">(<span class="params"><span class="keyword">let</span> i , <span class="keyword">let</span> j</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (i === <span class="number">-1</span> || j === <span class="number">-1</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="comment">//先初始化空串的base case</span></span><br><span class="line">        <span class="keyword">if</span> (str1[i] === str[j])&#123;</span><br><span class="line">            <span class="comment">//找到一个lcs的元素则继续往前找</span></span><br><span class="line">            <span class="keyword">return</span> dp(i - <span class="number">1</span>, j - <span class="number">1</span>) + <span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span>&#123;</span><br><span class="line">            <span class="comment">//否则，则找出让lcs最长的</span></span><br><span class="line">            <span class="keyword">return</span> max(dp(i - <span class="number">1</span>, j), dp(i, j - <span class="number">1</span>));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> dp(str1.length - <span class="number">1</span>, str2.length - <span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以通过备忘录或者DPtable来优化时间复杂度，</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> longestCommonSubsequencr = <span class="function">(<span class="params">str1, str2</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> m = str1.length, n = str2.length;</span><br><span class="line">    <span class="keyword">let</span> dp = <span class="keyword">new</span> <span class="built_in">Array</span>();</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; m; i++)&#123;</span><br><span class="line">        dp[i] = <span class="keyword">new</span> <span class="built_in">Array</span>();</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> j = <span class="number">0</span>; j &lt; n; j++)&#123;</span><br><span class="line">            dp[i][j] = <span class="number">0</span>; </span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//构建DPtable并初始化</span></span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; m; i++)&#123;</span><br><span class="line">        <span class="keyword">for</span> (j = <span class="number">0</span>; j &lt; n; j++)&#123;</span><br><span class="line">            <span class="keyword">if</span> (str[i] === str[j])&#123;</span><br><span class="line">                dp[i][j] = <span class="number">1</span> + dp[i - <span class="number">1</span>][j - <span class="number">1</span>];</span><br><span class="line">            &#125; <span class="keyword">else</span>&#123;</span><br><span class="line">                dp[i][j] = max(dp[i - <span class="number">1</span>][j], dp[i][j - <span class="number">1</span>]);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> dp[m - <span class="number">1</span>][n - <span class="number">1</span>];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>对于str1[i]与str2[j]不等的情况，是否可能两个字符都不在，但其实因为后面的选择max始终选择最大的，因此不用考虑，必然被排除在外。先像文本一样写出伪代码来定义DPtable理清逻辑，思考每个状态有哪些选择，用正确逻辑作出正确判断就能做出正确选择。</p>
<h2 id="经典动态规划：编辑距离"><a href="#经典动态规划：编辑距离" class="headerlink" title="经典动态规划：编辑距离"></a>经典动态规划：编辑距离</h2><p>给定两个字符串s1和s2，计算出将s1转换成s2所使用的最少操作数，可用操作只有插入、删除、替换一个字符。</p>
<p>思路：解决两个字符串的动态规划问题，一般都是用2个指针i、j分别指向两个字符串的最后，然后一步步往前走，缩小问题的规模；</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*base case:当i走完s1或者j走完s2时，可以直接返回另一个字符串剩下的长度；</span></span><br><span class="line"><span class="comment">if s1[i] == s2[j]:</span></span><br><span class="line"><span class="comment">	啥也不做(skip)</span></span><br><span class="line"><span class="comment">	i、j同时移动</span></span><br><span class="line"><span class="comment">else:</span></span><br><span class="line"><span class="comment">	三选一：</span></span><br><span class="line"><span class="comment">		插入、删除、替换</span></span><br><span class="line"><span class="comment">三选一选择的标准是：均尝试一遍，哪个操作最后得到的编辑距离最小。*/</span></span><br><span class="line"><span class="keyword">const</span> minDistance = <span class="function">(<span class="params">s1, s2</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> dp = <span class="function">(<span class="params">i, j</span>) =&gt;</span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (i == <span class="number">-1</span>) <span class="keyword">return</span> j + <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span> (j == <span class="number">-1</span>) <span class="keyword">return</span> i + <span class="number">1</span>;</span><br><span class="line">        <span class="comment">//定义base case</span></span><br><span class="line">        <span class="keyword">if</span> (s1[i] == s2[j]) <span class="keyword">return</span> dp(i - <span class="number">1</span>, j - <span class="number">1</span>);</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">return</span> min(dp(i, j - <span class="number">1</span>) + <span class="number">1</span>，dp(i - <span class="number">1</span>, j) + <span class="number">1</span>, dp(i - <span class="number">1</span>, j - <span class="number">1</span>) + <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> dp(s1.length - <span class="number">1</span>, s2.length - <span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>dp(i, j)返回s1[0..i]和s2[0..j]的最小编辑距离；同样，上述的解法属于暴力破解，需要使用动态规划技巧来优化重叠子问题</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> minDistance = <span class="function">(<span class="params">s1, s2</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> m = str1.length, n = str2.length;</span><br><span class="line">    <span class="keyword">let</span> dp = <span class="keyword">new</span> <span class="built_in">Array</span>();</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; m; i++)&#123;</span><br><span class="line">        dp[i] = <span class="keyword">new</span> <span class="built_in">Array</span>();</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> j = <span class="number">0</span>; j &lt; n; j++)&#123;</span><br><span class="line">            dp[i][j] = <span class="number">0</span>; </span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//构建DPtable并初始化</span></span><br><span class="line">    <span class="keyword">for</span> (int i = <span class="number">1</span>; i &lt;= m; i++)&#123;</span><br><span class="line">        dp[i][<span class="number">0</span>] = i;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (int j = <span class="number">1</span>; j &lt;= n; j++)&#123;</span><br><span class="line">        dp[<span class="number">0</span>][j] = j;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//进行合适的DPtable初始化，DPtable自底向上求解，递归从上至下</span></span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">1</span>; i &lt;= m; i++)&#123;</span><br><span class="line">        <span class="keyword">for</span> (j = <span class="number">1</span>; j &lt;= n; j++)&#123;</span><br><span class="line">            <span class="keyword">if</span> (s1[i] === s2[j])&#123;</span><br><span class="line">                dp[i][j] = dp[i - <span class="number">1</span>][j - <span class="number">1</span>];</span><br><span class="line">            &#125; <span class="keyword">else</span>&#123;</span><br><span class="line">                dp[i][j] = min(dp[i - <span class="number">1</span>][j] + <span class="number">1</span>, dp[i][j - <span class="number">1</span>] + <span class="number">1</span>, dp[i - <span class="number">1</span>][j - <span class="number">1</span>] + <span class="number">1</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> dp[m][n];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>一般来说，处理两个字符串的动态规划问题，均按照本文思路处理，建立DPtable；但这里仅给出了最小编辑距离，具体如何修改则需要给dp数组增加额外的信息。则使用JS中的对象数组，dp[i] [j]中存储的是一个对象</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arrayObj = [&#123;&#125;];<span class="comment">//定义对象数组</span></span><br><span class="line"><span class="keyword">let</span> m = str1.length, n = str2.length;</span><br><span class="line">    <span class="keyword">let</span> dp = <span class="keyword">new</span> <span class="built_in">Array</span>();</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; m; i++)&#123;</span><br><span class="line">        dp[i] = <span class="keyword">new</span> <span class="built_in">Array</span>();</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> j = <span class="number">0</span>; j &lt; n; j++)&#123;</span><br><span class="line">            dp[i][j] = &#123;</span><br><span class="line">                <span class="string">'val'</span> : <span class="number">0</span>,</span><br><span class="line">                <span class="string">'choice'</span> : <span class="number">0</span>,</span><br><span class="line">                <span class="comment">//val为其原本dp数组值，choice为操作，0代表什么也不做，1代表插入，2代表删除，3代表替换</span></span><br><span class="line">            &#125;; </span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"><span class="comment">//最终结果为dp[m][n]，后续则一步步反推，插入=&gt;左移、替换=&gt;左移并上移、删除=&gt;上移,形成一条路径；这样一步步走回来了。</span></span><br></pre></td></tr></table></figure>



<h2 id="动态规划的子序列模板（最长回文子序列）"><a href="#动态规划的子序列模板（最长回文子序列）" class="headerlink" title="动态规划的子序列模板（最长回文子序列）"></a>动态规划的子序列模板（最长回文子序列）</h2><p>子序列通常涉及两个字符串，例如：两个字符串的最长公共子序列；既然要用动态规划，关键在于dp数组的定义与寻找状态转移关系，不同问题需要使用不同的dp数组定义。</p>
<h3 id="一、一维的dp数组"><a href="#一、一维的dp数组" class="headerlink" title="一、一维的dp数组"></a>一、一维的dp数组</h3><p>最长递增子序列LIS：<strong>在子数组array[0…i]中，以array[i]为结尾的目标子序列的长度是dp[i]。</strong></p>
<p>目的：符合归纳法，能够从前n-1个的结果中推断出来第n个的结果</p>
<h3 id="二、二维的dp数组"><a href="#二、二维的dp数组" class="headerlink" title="二、二维的dp数组"></a>二、二维的dp数组</h3><h4 id="涉及两个字符串、数组时："><a href="#涉及两个字符串、数组时：" class="headerlink" title="涉及两个字符串、数组时："></a>涉及两个字符串、数组时：</h4><p><strong>在子数组arr1[0…i]和子数组arr2[0…j]中，我们要求的子序列(最长公共子序列)长度为dp[i] [j];</strong></p>
<h4 id="只涉及一个字符串、数组："><a href="#只涉及一个字符串、数组：" class="headerlink" title="只涉及一个字符串、数组："></a>只涉及一个字符串、数组：</h4><p>在子数组array[i…j]中，我们要求的子序列(最长回文子序列)的长度为dp[i] [j]。</p>
<h3 id="最长回文子序列"><a href="#最长回文子序列" class="headerlink" title="最长回文子序列"></a>最长回文子序列</h3><p>DPtable的定义：在子串s[i…j]中，最长回文子序列的长度为dp[i] [j]；找状态转移方程的关键在于归纳思维，即如何从已知的结果推断出未知的部分。</p>
<p>具体来说，如果我们想求dp[i] [j]，假设已经知道子问题dp[i + 1] [j - 1]的结果，是否能想办法算出dp[i] [j]的值。可以，取决于s[i]和s[j]的字符。1、如果他两相等则它们加上便就是当前的最大回文子序列；2、如果她两不相等，说明两者不可能同时出现在最长回文子序列中，于是把它们分别加入s[i+1…j-1]中，看看哪个子串产生的回文子序列更长即可。</p>
<p>base case：dp[i] [j] = 1;</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> longestPalindromeSubseq = <span class="function">(<span class="params">str1</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> m = str1.length;</span><br><span class="line">    <span class="keyword">let</span> dp = <span class="keyword">new</span> <span class="built_in">Array</span>();</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; m; i++)&#123;</span><br><span class="line">        dp[i] = <span class="keyword">new</span> <span class="built_in">Array</span>();</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> j = <span class="number">0</span>; j &lt; n; j++)&#123;</span><br><span class="line">            dp[i][j] = <span class="number">0</span>; </span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; n; i++)&#123;</span><br><span class="line">        dp[i][i] = <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//根据上面的状态转移方程，为保证每次计算dp[i][j]，左、下、左下三个方向的位置均被计算出来，只能斜着遍历或反着遍历。</span></span><br><span class="line">    <span class="keyword">for</span> (i = n - <span class="number">1</span>; i &gt;= <span class="number">0</span>; i--)&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> j = i + <span class="number">1</span>; j &lt; n; j++)&#123;</span><br><span class="line">            <span class="keyword">if</span> (s[i] == s[j])&#123;</span><br><span class="line">                dp[i][j] = dp[i + <span class="number">1</span>][j - <span class="number">1</span>] + <span class="number">2</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span>&#123;</span><br><span class="line">                dp[i][j] = max(dp[i + <span class="number">1</span>][j], dp[i][j - <span class="number">1</span>]);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> dp[<span class="number">0</span>][n - <span class="number">1</span>];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="动态规划的正则表达式"><a href="#动态规划的正则表达式" class="headerlink" title="动态规划的正则表达式"></a>动态规划的正则表达式</h2><p>算法的设计是⼀个螺旋上升、逐步求精 的过程，绝不是⼀步到位就能写出正确算法。</p>
<p><strong>写递归的技巧是管好当下，之后的事抛给递归</strong></p>
<h2 id="动态规划答疑篇"><a href="#动态规划答疑篇" class="headerlink" title="动态规划答疑篇"></a>动态规划答疑篇</h2><h3 id="最优子结构"><a href="#最优子结构" class="headerlink" title="最优子结构"></a>最优子结构</h3><p>最优子结构：从子问题的最优结果能够推断出更大规模问题的最优结果；想要满足最优子结构，子问题之间必须相互独立。遇到这种最优子结构失效的情况，策略是改造问题。等价转化：最大分数差&lt;=&gt;最高分数和最低分数的差&lt;=&gt;求最高与最低分数。</p>
<p>最优子结构并不是动态规划独有的性质，但反过来，动态规划一定是要求的求最值的。动态规划就是通过base case一步步反推往上，只有符合最优子结构的问题才有发生这种链式反应的性质。</p>
<p>找寻最优子结构的过程其实就是证明状态转移方程正确性的过程，方程符合最优子结构就可以写暴力解了，写出暴力解就能看出有没有重叠子问题，有则优化。</p>
<h3 id="dp数组的遍历方向"><a href="#dp数组的遍历方向" class="headerlink" title="dp数组的遍历方向"></a>dp数组的遍历方向</h3><p>对dp数组的遍历方向有所困惑，可以正向、反向遍历，乃至斜向遍历；</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//斜着遍历</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> l = <span class="number">2</span>; l &lt;= n; l++)&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt;= n - <span class="number">1</span>; i++)&#123;</span><br><span class="line">        <span class="keyword">let</span> j = l + i - <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>dp遍历设置的两大原则：1、遍历的过程中，所需的状态必须是已经计算出来的；2、遍历的终点必须是存储结果的那个位置；</p>
<p>即根据base case处于的位置来判断dp数组计算的起步，根据递推关系来判断dp数组递推的方向。</p>
<h2 id="动态规划二维变一维"><a href="#动态规划二维变一维" class="headerlink" title="动态规划二维变一维"></a>动态规划二维变一维</h2><h2 id="动态规划之股票买卖"><a href="#动态规划之股票买卖" class="headerlink" title="动态规划之股票买卖"></a>动态规划之股票买卖</h2><p>题目：给定一个数组，它的第i个元素是一支给定的股票在第i天的价格，设计一个算法来计算你所能获取的最大利润，且你最多可以完成k笔交易。（你不能同时参与多笔交易，必须在再次购买之前出售）</p>
<h3 id="穷举框架"><a href="#穷举框架" class="headerlink" title="穷举框架"></a>穷举框架</h3><p>这⾥，我们不⽤递归思想进⾏穷举，⽽是利⽤「状态」进⾏穷举。我们具 体到每⼀天，看看总共有⼏种可能的「状态」，再找出每个「状态」对应的 「选择」。我们要穷举所有「状态」，穷举的⽬的是根据对应的「选择」更 新状态。</p>
<p>⽐如说这个问题，<strong>每天都有三种「选择」</strong>：买⼊、卖出、⽆操作，我们⽤ buy, sell, rest 表⽰这三种选择。但问题是，并不是每天都可以任意选择这三 种选择的，因为 sell 必须在 buy 之后，buy 必须在 sell 之后。那么 rest 操作 还应该分两种状态，⼀种是 buy 之后的 rest（持有了股票），⼀种是 sell 之 后的 rest（没有持有股票）。⽽且别忘了，我们还有交易次数 k 的限制，就 是说你 buy 还只能在 k &gt; 0 的前提下操作。</p>
<p><strong>这个问题的「状态」有三个</strong>，第⼀个是天 数，第⼆个是允许交易的最⼤次数，第三个是当前的持有状态（即之前说的 rest 的状态，我们不妨⽤ 1 表⽰持有，0 表⽰没有持有）。然后我们⽤⼀个 三维数组就可以装下这⼏种状态的全部组合： </p>
<p>⽽且我们可以⽤⾃然语⾔描述出每⼀个状态的含义，⽐如说 dp[3][2][1] 的含义就是：今天是第三天，我现在⼿上持有着股票，⾄今最多进⾏ 2 次交 易。再⽐如 dp [2] [3] [0] 的含义：今天是第⼆天，我现在⼿上没有持有股 票，⾄今最多进⾏ 3 次交易。很容易理解，对吧？ </p>
<p>我们想求的最终答案是 dp [n - 1] [K] [0]，即最后⼀天，最多允许 K 次交易， 最多获得多少利润。读者可能问为什么不是 dp[n - 1] [K] [1]？因为 [1] 代表⼿ 上还持有股票，[0] 表⽰⼿上的股票已经卖出去了，很显然后者得到的利润 ⼀定⼤于前者。 </p>
<p>记住如何解释「状态」，⼀旦你觉得哪⾥不好理解，把它翻译成⾃然语⾔就 容易理解了。 </p>
<h3 id="状态转移框架"><a href="#状态转移框架" class="headerlink" title="状态转移框架"></a>状态转移框架</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">dp[i][k][<span class="number">0</span>] = max(dp[i<span class="number">-1</span>][k][<span class="number">0</span>], dp[i<span class="number">-1</span>][k][<span class="number">1</span>] + prices[i]) </span><br><span class="line">max( 选择 rest , 选择 sell ) </span><br><span class="line">解释：今天我没有持有股票，有两种可能： 要么是我昨天就没有持有，然后今天选择 rest，所以我今天还是没有持有； 要么是我昨天持有股票，但是今天我 sell 了，所以我今天没有持有股票了。 </span><br><span class="line"></span><br><span class="line">dp[i][k][<span class="number">1</span>] = max(dp[i<span class="number">-1</span>][k][<span class="number">1</span>], dp[i<span class="number">-1</span>][k<span class="number">-1</span>][<span class="number">0</span>] - prices[i]) </span><br><span class="line">max( 选择 rest , 选择 buy ) </span><br><span class="line">解释：今天我持有着股票，有两种可能： 要么我昨天就持有着股票，然后今天选择 rest，所以我今天还持有着股票； 要么我昨天本没有持有，但今天我选择 buy，所以今天我就持有股票了</span><br></pre></td></tr></table></figure>

<p>现在，我们已经完成了动态规划中最困难的⼀步：状态转移⽅程。<strong>如果之前</strong> <strong>的内容你都可以理解，那么你已经可以秒杀所有问题了，只要套这个框架就</strong> <strong>⾏了。</strong>不过还差最后⼀点点，就是定义 base case，即最简单的情况</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">dp[<span class="number">-1</span>][k][<span class="number">0</span>] = <span class="number">0</span> </span><br><span class="line">解释：因为 i 是从 <span class="number">0</span> 开始的，所以 i = <span class="number">-1</span> 意味着还没有开始，这时候的利润当然是 <span class="number">0</span> 。</span><br><span class="line">dp[<span class="number">-1</span>][k][<span class="number">1</span>] = -infinity </span><br><span class="line">解释：还没开始的时候，是不可能持有股票的，⽤负⽆穷表⽰这种不可能。 </span><br><span class="line">dp[i][<span class="number">0</span>][<span class="number">0</span>] = <span class="number">0</span> </span><br><span class="line">解释：因为 k 是从 <span class="number">1</span> 开始的，所以 k = <span class="number">0</span> 意味着根本不允许交易，这时候利润当然是 <span class="number">0</span> 。</span><br><span class="line">dp[i][<span class="number">0</span>][<span class="number">1</span>] = -infinity </span><br><span class="line">解释：不允许交易的情况下，是不可能持有股票的，⽤负⽆穷表⽰这种不可能</span><br></pre></td></tr></table></figure>

<p>总结一下：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">base case:</span><br><span class="line">    dp[<span class="number">-1</span>][k][<span class="number">0</span>] = dp[i][<span class="number">0</span>][<span class="number">0</span>]</span><br><span class="line">    dp[<span class="number">-1</span>][k][<span class="number">0</span>] = dp[i][<span class="number">0</span>][<span class="number">1</span>] = -infinity</span><br><span class="line">    </span><br><span class="line">状态转移方程:</span><br><span class="line">    dp[i][k][<span class="number">0</span>] = max(dp[i<span class="number">-1</span>][k][<span class="number">0</span>], dp[i<span class="number">-1</span>][k][<span class="number">1</span>] + prices[i])</span><br><span class="line">    dp[i][k][<span class="number">1</span>] = max(dp[i<span class="number">-1</span>][k][<span class="number">1</span>], dp[i<span class="number">-1</span>][k<span class="number">-1</span>][<span class="number">0</span>] - prices[i])</span><br></pre></td></tr></table></figure>



<h2 id="k-1时"><a href="#k-1时" class="headerlink" title="k=1时"></a>k=1时</h2><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; n; i++) &#123; </span><br><span class="line">    <span class="keyword">if</span> (i - <span class="number">1</span> == <span class="number">-1</span>)&#123; </span><br><span class="line">        dp[i][<span class="number">0</span>] = <span class="number">0</span>; </span><br><span class="line">        <span class="comment">// 解释： </span></span><br><span class="line">        <span class="comment">// dp[i][0] </span></span><br><span class="line">        <span class="comment">// = max(dp[-1][0], dp[-1][1] + prices[i]) </span></span><br><span class="line">        <span class="comment">// = max(0, -infinity + prices[i]) = 0 </span></span><br><span class="line">        dp[i][<span class="number">1</span>] = -prices[i]; </span><br><span class="line">        <span class="comment">//解释： </span></span><br><span class="line">        <span class="comment">// dp[i][1] </span></span><br><span class="line">        <span class="comment">// = max(dp[-1][1], dp[-1][0] - prices[i]) </span></span><br><span class="line">        <span class="comment">// = max(-infinity, 0 - prices[i]) </span></span><br><span class="line">        <span class="comment">// = -prices[i] </span></span><br><span class="line">        <span class="keyword">continue</span>; </span><br><span class="line">    &#125;</span><br><span class="line">    dp[i][<span class="number">0</span>] = Math.<span class="built_in">max</span>(dp[i<span class="number">-1</span>][<span class="number">0</span>], dp[i<span class="number">-1</span>][<span class="number">1</span>] + prices[i]); </span><br><span class="line">    dp[i][<span class="number">1</span>] = Math.<span class="built_in">max</span>(dp[i<span class="number">-1</span>][<span class="number">1</span>], -prices[i]); </span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> dp[n - <span class="number">1</span>][<span class="number">0</span>];</span><br></pre></td></tr></table></figure>

<p>第⼀题就解决了，但是这样处理 base case 很⿇烦，⽽且注意⼀下状态转移 ⽅程，新状态只和相邻的⼀个状态有关，其实不⽤整个 dp 数组，只需要⼀ 个变量储存相邻的那个状态就⾜够了，这样可以把空间复杂度降到 O(1):</p>
<h2 id="k-infinity"><a href="#k-infinity" class="headerlink" title="k= +infinity"></a>k= +infinity</h2><p>k为正无穷，则可以把k、k-1看作一样，同样可以改写框架，数组中k已经不会改变，则不需要记录k。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">maxProfit_k_inf</span><span class="params">(<span class="keyword">int</span>[] prices)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> n = prices.length; </span><br><span class="line">    <span class="keyword">int</span> dp_i_0 = <span class="number">0</span>, dp_i_1 = Integer.MIN_VALUE; </span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; n; i++) &#123; </span><br><span class="line">        <span class="keyword">int</span> temp = dp_i_0; </span><br><span class="line">        dp_i_0 = Math.<span class="built_in">max</span>(dp_i_0, dp_i_1 + prices[i]); </span><br><span class="line">        dp_i_1 = Math.<span class="built_in">max</span>(dp_i_1, temp - prices[i]); </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> dp_i_0; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="k-infinity-coldDown"><a href="#k-infinity-coldDown" class="headerlink" title="k = infinity + coldDown"></a>k = infinity + coldDown</h2><p>每次 sell 之后要等⼀天才能继续交易。只要把这个特点融⼊上⼀题的状态转 移⽅程即可： </p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">dp[i][<span class="number">0</span>] = <span class="built_in">max</span>(dp[i<span class="number">-1</span>][<span class="number">0</span>], dp[i<span class="number">-1</span>][<span class="number">1</span>] + prices[i]) ;</span><br><span class="line">dp[i][<span class="number">1</span>] = <span class="built_in">max</span>(dp[i<span class="number">-1</span>][<span class="number">1</span>], dp[i<span class="number">-2</span>][<span class="number">0</span>] - prices[i]);</span><br><span class="line"><span class="comment">//解释：第 i 天选择 buy 的时候，要从 i-2 的状态转移，⽽不是 i-1 。</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">maxProfit_with_cool</span><span class="params">(<span class="keyword">int</span>[] prices)</span> </span>&#123; </span><br><span class="line">    <span class="keyword">int</span> n = prices.length; </span><br><span class="line">    <span class="keyword">int</span> dp_i_0 = <span class="number">0</span>, dp_i_1 = Integer.MIN_VALUE; </span><br><span class="line">    <span class="keyword">int</span> dp_pre_0 = <span class="number">0</span>; </span><br><span class="line">    <span class="comment">// 代表 dp[i-2][0] </span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; n; i++) &#123; </span><br><span class="line">        <span class="keyword">int</span> temp = dp_i_0; </span><br><span class="line">        dp_i_0 = Math.<span class="built_in">max</span>(dp_i_0, dp_i_1 + prices[i]); </span><br><span class="line">        dp_i_1 = Math.<span class="built_in">max</span>(dp_i_1, dp_pre_0 - prices[i]); </span><br><span class="line">        dp_pre_0 = temp; </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> dp_i_0; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="k-infinity-with-fee"><a href="#k-infinity-with-fee" class="headerlink" title="k = +infinity with fee"></a>k = +infinity with fee</h2><p>每次交易要⽀付⼿续费，只要把⼿续费从利润中减去即可。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">dp[i][<span class="number">0</span>] = <span class="built_in">max</span>(dp[i<span class="number">-1</span>][<span class="number">0</span>], dp[i<span class="number">-1</span>][<span class="number">1</span>] + prices[i]) ;</span><br><span class="line">dp[i][<span class="number">1</span>] = <span class="built_in">max</span>(dp[i<span class="number">-1</span>][<span class="number">1</span>], dp[i<span class="number">-1</span>][<span class="number">0</span>] - prices[i] - fee); </span><br><span class="line"><span class="comment">//解释：相当于买⼊股票的价格升⾼了。 在第⼀个式⼦⾥减也是⼀样的，相当于卖出股票的价格减⼩了。</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">maxProfit_with_fee</span><span class="params">(<span class="keyword">int</span>[] prices, <span class="keyword">int</span> fee)</span> </span>&#123; </span><br><span class="line">    <span class="keyword">int</span> n = prices.length; </span><br><span class="line">    <span class="keyword">int</span> dp_i_0 = <span class="number">0</span>, </span><br><span class="line">    dp_i_1 = Integer.MIN_VALUE; </span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; n; i++) &#123; </span><br><span class="line">        <span class="keyword">int</span> temp = dp_i_0; </span><br><span class="line">        dp_i_0 = Math.<span class="built_in">max</span>(dp_i_0, dp_i_1 + prices[i]); </span><br><span class="line">        dp_i_1 = Math.<span class="built_in">max</span>(dp_i_1, temp - prices[i] - fee); </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> dp_i_0; &#125;</span><br></pre></td></tr></table></figure>

<h2 id="k-2"><a href="#k-2" class="headerlink" title="k = 2"></a><strong>k = 2</strong></h2><p>k = 2 和前⾯题⽬的情况稍微不同，因为上⾯的情况都和 k 的关系不太⼤。 要么 k 是正⽆穷，状态转移和 k 没关系了；要么 k = 1，跟 k = 0 这个 base case 挨得近，最后也没有存在感。 这道题 k = 2 和后⾯要讲的 k 是任意正整数的情况中，对 k 的处理就凸显出 来了。</p>
<p>这道题由于没有消掉 k 的影响，所以必须要对 k 进⾏穷举：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> max_k = <span class="number">2</span>; </span><br><span class="line"><span class="keyword">int</span>[][][] dp = <span class="keyword">new</span> <span class="keyword">int</span>[n][max_k + <span class="number">1</span>][<span class="number">2</span>]; </span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; n; i++) &#123; </span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> k = max_k; k &gt;= <span class="number">1</span>; k--) &#123; </span><br><span class="line">        <span class="keyword">if</span> (i - <span class="number">1</span> == <span class="number">-1</span>) &#123; </span><br><span class="line">            <span class="comment">/*处理 base case */</span> </span><br><span class="line">        &#125; </span><br><span class="line">        dp[i][k][<span class="number">0</span>] = <span class="built_in">max</span>(dp[i<span class="number">-1</span>][k][<span class="number">0</span>], dp[i<span class="number">-1</span>][k][<span class="number">1</span>] + prices[i]); </span><br><span class="line">        dp[i][k][<span class="number">1</span>] = <span class="built_in">max</span>(dp[i<span class="number">-1</span>][k][<span class="number">1</span>], dp[i<span class="number">-1</span>][k<span class="number">-1</span>][<span class="number">0</span>] - prices[i]); </span><br><span class="line">    &#125; </span><br><span class="line">&#125;<span class="comment">// 穷举了 n × max_k × 2 个状态，正确。 </span></span><br><span class="line"><span class="keyword">return</span> dp[n - <span class="number">1</span>][max_k][<span class="number">0</span>];</span><br></pre></td></tr></table></figure>



<h2 id="k-any-integer"><a href="#k-any-integer" class="headerlink" title="k = any integer"></a><strong>k = any integer</strong></h2><p>有了上⼀题 k = 2 的铺垫，这题应该和上⼀题的第⼀个解法没啥区别。但是 出现了⼀个超内存的错误，原来是传⼊的 k 值会⾮常⼤，dp 数组太⼤了。 现在想想，交易次数 k 最多有多⼤呢？ ⼀次交易由买⼊和卖出构成，⾄少需要两天。所以说有效的限制 k 应该不超 过 n/2，如果超过，就没有约束作⽤了，相当于 k = +infinity。这种情况是之 前解决过的。 直接把之前的代码重⽤： </p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">maxProfit_k_any</span><span class="params">(<span class="keyword">int</span> max_k, <span class="keyword">int</span>[] prices)</span> </span>&#123; </span><br><span class="line">    <span class="keyword">int</span> n = prices.length; </span><br><span class="line">    <span class="keyword">if</span> (max_k &gt; n / <span class="number">2</span>) </span><br><span class="line">        <span class="keyword">return</span> maxProfit_k_inf(prices); </span><br><span class="line">    <span class="keyword">int</span>[][][] dp = <span class="keyword">new</span> <span class="keyword">int</span>[n][max_k + <span class="number">1</span>][<span class="number">2</span>]; </span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; n; i++) </span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> k = max_k; k &gt;= <span class="number">1</span>; k--) &#123; </span><br><span class="line">            <span class="keyword">if</span> (i - <span class="number">1</span> == <span class="number">-1</span>) &#123; </span><br><span class="line">                <span class="comment">/* 处理 base case */</span> </span><br><span class="line">            &#125; </span><br><span class="line">            dp[i][k][<span class="number">0</span>] = <span class="built_in">max</span>(dp[i<span class="number">-1</span>][k][<span class="number">0</span>], dp[i<span class="number">-1</span>][k][<span class="number">1</span>] + prices[i0]); </span><br><span class="line">            dp[i][k][<span class="number">1</span>] = <span class="built_in">max</span>(dp[i<span class="number">-1</span>][k][<span class="number">1</span>], dp[i<span class="number">-1</span>][k<span class="number">-1</span>][<span class="number">0</span>] - prices;</span><br></pre></td></tr></table></figure>



<h1 id="动态规划与回溯对比"><a href="#动态规划与回溯对比" class="headerlink" title="动态规划与回溯对比"></a>动态规划与回溯对比</h1><p>题目：给定一个非负整数组，a1,a2,…an和一个目标数，S。现在你有两个符号+和-；对于数组中的任意一个整数，你都可以添加一个符号到前面，返回可以使最终数组和为目标数S的所有添加符号的方法数。</p>
<h2 id="回溯思路"><a href="#回溯思路" class="headerlink" title="回溯思路"></a>回溯思路</h2><p>回溯算法就是一个暴力穷举算法；关键在于1、路径（即已经做出的选择），2、选择列表（当前可以做的选择），3、结束条件（到达决策树底层，无法再做选择的条件）。</p>
<p>1、路径：之前已经给前面的数字写出的正负号；2、选择列表：即每个数字可以选择给出正号或者负号；3、结束条件：即已经走到了最后一个数字；</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> result = <span class="number">0</span>;</span><br><span class="line"><span class="comment">//主函数</span></span><br><span class="line"><span class="keyword">const</span> findTargetSumWays = <span class="function">(<span class="params">nums, target</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (nums.length == <span class="number">0</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    backtrack(nums, <span class="number">0</span>, target);</span><br><span class="line">    <span class="keyword">return</span> result</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">const</span> backtrack = (nums, i, rest)&#123;</span><br><span class="line">        <span class="comment">//base case</span></span><br><span class="line">        <span class="keyword">if</span> (i == nums.length)&#123;</span><br><span class="line">            <span class="keyword">if</span> (rest == <span class="number">0</span>)&#123;</span><br><span class="line">                <span class="comment">//说明恰好凑出target</span></span><br><span class="line">                result++;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//给nums[i]选择-号</span></span><br><span class="line">        rest += nums[i];</span><br><span class="line">        <span class="comment">//递归来穷举nums[i+1]的可能性</span></span><br><span class="line">        backtrack(nums, i + <span class="number">1</span>, rest);</span><br><span class="line">        <span class="comment">//撤销选择</span></span><br><span class="line">        rest -= nums[i];</span><br><span class="line">        </span><br><span class="line">        <span class="comment">//给nums[i]选择+号</span></span><br><span class="line">        rest -= nums[i];</span><br><span class="line">        <span class="comment">//穷举nums[i+1]</span></span><br><span class="line">        backtrack(nums, i + <span class="number">1</span>, rest);</span><br><span class="line">        <span class="comment">//撤销选择</span></span><br><span class="line">        rest += nums[i];</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>时间复杂度为O(2^N)，N为nums的大小，其实这个回溯算法就是个二叉树的遍历问题，树的高度就是nums的长度，故时间复杂度就是这棵二叉树的节点数，其实比较低效。</p>
<h2 id="消除重叠子问题"><a href="#消除重叠子问题" class="headerlink" title="消除重叠子问题"></a>消除重叠子问题</h2><p>动态规划快的原因就是消除了重叠子问题，关键在于看是否可能出现重复的状态；对于递归函数来说，函数参数中会变得参数就是状态，对于backtrack函数即i和rest。那么怎么一眼看出存在重叠子问题？</p>
<p>1、先抽象出算法得递归框架；</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">dp</span><span class="params">(i, j)</span>:</span></span><br><span class="line">    dp(i - <span class="number">1</span>, j - <span class="number">1</span>)<span class="comment">#1</span></span><br><span class="line">    dp(i, j - <span class="number">1</span>)<span class="comment">#2</span></span><br><span class="line">    dp(i - <span class="number">1</span>, j)<span class="comment">#4</span></span><br></pre></td></tr></table></figure>

<p>2、对于子问题dp(i-1, j-1)，如何通过原问题dp(i, j)来得到呢？有多种不同路径:比如dp(i,j)=&gt;#1，和dp(i, j)=&gt;#2 =&gt;#3。一旦发现一条重复路径就说明存在巨量重复路径，也就是重叠子问题。</p>
<p>对于上题：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> backtrack(int i, int rest)&#123;</span><br><span class="line">    backtrack(i + <span class="number">1</span>，rest - nums[i]);</span><br><span class="line">    backtrack(i + <span class="number">1</span>, rest + nums[i]);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//当nums[i]=0时，出现两种状态完全相同得递归函数，这就是重叠子问题</span></span><br><span class="line"><span class="comment">//因此状态(i, rest)可以用备忘录来优化</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> fingTargetSumWays = <span class="function">(<span class="params">nums, target</span>) =&gt;</span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (nums.length == <span class="number">0</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">return</span> dp(nums, <span class="number">0</span>, target);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//备忘录</span></span><br><span class="line"><span class="keyword">const</span> memo = <span class="keyword">new</span> <span class="built_in">Map</span>();</span><br><span class="line"><span class="keyword">const</span> dp = <span class="function">(<span class="params">nums, i, rest</span>) =&gt;</span>&#123;</span><br><span class="line">    <span class="comment">//base case</span></span><br><span class="line">    <span class="keyword">if</span> (i == nums.length) &#123;</span><br><span class="line">        <span class="keyword">if</span> (rest == <span class="number">0</span>) <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//避免重复计算,转换成字符串从而作为Map的键来存储</span></span><br><span class="line">    <span class="keyword">let</span> key = i.toString() + <span class="string">","</span> + rest.toString；</span><br><span class="line">    <span class="keyword">if</span> (memo.has(key)) &#123;</span><br><span class="line">        <span class="keyword">return</span> memo.get(key);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//还是穷举</span></span><br><span class="line">    <span class="keyword">let</span> result = dp(nums, i + <span class="number">1</span>, rest - nums[i]) + dp(nums, i + <span class="number">1</span>, rest + nums[i]);</span><br><span class="line">    <span class="comment">//这两种情况，"+"或者"-"号</span></span><br><span class="line">    memo.set(key, result);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="动态规划"><a href="#动态规划" class="headerlink" title="动态规划"></a>动态规划</h2><p>上述的消除重叠子问题的方案，在最坏情况下的时间复杂度仍为O(2^N)，因为底层逻辑没变，暴力穷举，加上顺手进行了剪枝，提升了算法在某些情况的效率，但不算质的飞跃。其实，这个问题可以转化成一个子集划分的问题，而子集划分问题又是一个典型的背包问题。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//我们把nums划分为两个子集A、B，分别代表分配+的数和分配-的数，则它们跟target存在如下关系：</span></span><br><span class="line">sum(A) - sum(B) = target</span><br><span class="line">sum(A) = target + sum(B)</span><br><span class="line">sum(A) + sum(A) = target + sum(B) + sum(A)</span><br><span class="line"><span class="number">2</span> * sum(A) = target + sum(nums)</span><br><span class="line"><span class="comment">//因此可以把问题转化为：nums中存在几个子集A，使得A中元素的和为(target + sum(nums)) / 2。</span></span><br><span class="line"><span class="comment">//这样就变成了经典的背包问题</span></span><br><span class="line"><span class="comment">//dp[i][j]代表前i个数构成结果为j的方法个数</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> fingTargetSumWays = <span class="function">(<span class="params">nums, target</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> sum = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; nums.length; i++)&#123;</span><br><span class="line">        sum += nums[i];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">let</span> len = nums.length;</span><br><span class="line">    sum = (sum + target) / <span class="number">2</span>;</span><br><span class="line">    <span class="keyword">let</span> dp = <span class="keyword">new</span> <span class="built_in">Array</span>();</span><br><span class="line">    <span class="comment">//根据初始条件来初始化</span></span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt;= len + <span class="number">1</span>; i++) &#123;</span><br><span class="line">        dp[i] = <span class="keyword">new</span> <span class="built_in">Array</span>();</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">let</span> j = <span class="number">0</span>; j &lt;= sum + <span class="number">1</span>; j++)&#123;</span><br><span class="line">            dp[i][j] = <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (j = <span class="number">0</span>; j &lt;= n; i++)&#123;</span><br><span class="line">        dp[<span class="number">0</span>][j] = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//没有物品则没办法装背包，则为0</span></span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt;= n; i++)&#123;</span><br><span class="line">        dp[i][<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//背包的最大载重为0，则只有一种方法，即什么都不装</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">1</span>; i &lt;= nums.length; i++)&#123;</span><br><span class="line">        <span class="keyword">for</span> (j = <span class="number">1</span>; j &lt;= sum; j++)&#123;</span><br><span class="line">            <span class="keyword">if</span> (j - nums[i - <span class="number">1</span>] &lt; <span class="number">0</span>)&#123;</span><br><span class="line">                <span class="comment">//背包不足，不能装入第i个物品</span></span><br><span class="line">                dp[i][j] = dp[i - <span class="number">1</span>] [j];</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">//装入或不装入的选择</span></span><br><span class="line">                dp[i] [j] = dp[i - <span class="number">1</span>] [j] + dp[i - <span class="number">1</span>] [j - nums[i - <span class="number">1</span>]];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> dp[nums.length][sum];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>总结一下：回溯算法虽好，但是复杂度很高，即使消除一些冗杂计算也只是剪枝，没有本质的改变；而动态规划则比较玄学，经过各种改造后，从一个加减法问题变成了子集问题，又变成背包问题，经过各种套路写出解法，还得使用状态压缩，加上反向遍历。</p>
<h1 id="回溯算法框架"><a href="#回溯算法框架" class="headerlink" title="回溯算法框架"></a>回溯算法框架</h1><p>解决一个回溯问题，其实即使一个决策树的遍历过程，只需要思考3个问题，1、路径：就是已经做出的选择；2、选择列表：就是你当前可以做的选择；3、结束条件：到达决策树底层，无法再做选择的条件。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">result &#x3D; []</span><br><span class="line">def backtrack(路径，选择列表)</span><br><span class="line">	if 满足结束条件：</span><br><span class="line">		result.add(路径)</span><br><span class="line">		return</span><br><span class="line">	</span><br><span class="line">	for 选择 in 选择列表:</span><br><span class="line">		做选择</span><br><span class="line">		backtrack(路径，选择列表)</span><br><span class="line">		撤销选择</span><br></pre></td></tr></table></figure>

<p>核心其实就是for循环里面的递归，在递归调用前做选择，递归调用之后撤销选择。</p>
<h2 id="回溯之全排列问题"><a href="#回溯之全排列问题" class="headerlink" title="回溯之全排列问题"></a>回溯之全排列问题</h2><p>穷举n个不重复数的全排列，求有多少个。</p>
<p>则可以画出该算法的决策树如下，每个节点都在做决策。[2]就是路径，记录你已经做过的选择；[1，3]是选择列表，表示当前可以做出的选择；[结束条件]就是遍历到树的底层，即选择列表为空的时候。</p>
<p>我们定义的backtrack函数其实就像一个指针，在这棵树上游走同时正确维护每个节点的属性，每当走到树的底层，其路径就是一个全排列。</p>
<p><img src="/../../image/%E7%AE%97%E6%B3%95%E6%A1%86%E6%9E%B6/%E5%85%A8%E6%8E%92%E5%88%97.png" alt="全排列"></p>
<p>树的遍历问题：前序遍历、后序遍历。前序遍历代码在进入某一个节点之前的那个时间点执行，后序遍历代码在离开某个节点之后的那个时间点执行；函数在树上游走，正确维护其属性就是要在这两个特殊时间点搞动作。我们只要在递归之前做出选择，递归之后撤销选择，就能得到每个节点的选择列表、路径。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">List&lt;List&lt;Integer&gt;&gt; res = <span class="keyword">new</span> LinkedList&lt;&gt;(); </span><br><span class="line"><span class="comment">/* 主函数，输⼊⼀组不重复的数字，返回它们的全排列 */</span> </span><br><span class="line"><span class="function">List&lt;List&lt;Integer&gt;&gt; <span class="title">permute</span><span class="params">(<span class="keyword">int</span>[] nums)</span> </span>&#123; </span><br><span class="line">    <span class="comment">// 记录「路径」 </span></span><br><span class="line">    LinkedList&lt;Integer&gt; track = <span class="keyword">new</span> LinkedList&lt;&gt;(); </span><br><span class="line">    backtrack(nums, track); </span><br><span class="line">    <span class="keyword">return</span> res; </span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 路径：记录在 track 中 </span></span><br><span class="line"><span class="comment">// 选择列表：nums 中不存在于 track 的那些元素 </span></span><br><span class="line"><span class="comment">// 结束条件：nums 中的元素全都在 track 中出现 </span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">backtrack</span><span class="params">(<span class="keyword">int</span>[] nums, LinkedList&lt;Integer&gt; track)</span> </span>&#123; </span><br><span class="line">    <span class="comment">// 触发结束条件 </span></span><br><span class="line">    <span class="keyword">if</span> (track.<span class="built_in">size</span>() == nums.length) &#123; </span><br><span class="line">        res.add(<span class="keyword">new</span> LinkedList(track)); </span><br><span class="line">        <span class="keyword">return</span>; </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; nums.length; i++) &#123; </span><br><span class="line">        <span class="comment">// 排除不合法的选择 </span></span><br><span class="line">        <span class="keyword">if</span> (track.contains(nums[i])) </span><br><span class="line">            <span class="keyword">continue</span>; </span><br><span class="line">        <span class="comment">// 做选择 </span></span><br><span class="line">        track.add(nums[i]); </span><br><span class="line">        <span class="comment">// 进⼊下⼀层决策树 </span></span><br><span class="line">        backtrack(nums, track); </span><br><span class="line">        <span class="comment">// 取消选择</span></span><br><span class="line">        track.removeLast(); </span><br><span class="line">    &#125; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>我们这⾥稍微做了些变通，没有显式记录「选择列表」，⽽是通过 nums 和 track 推导出当前的选择列表：因为穷举整棵决策树是⽆法避免的。<strong>这也是回溯算法的⼀</strong> 个特点，不像动态规划存在重叠⼦问题可以优化，回溯算法就是纯暴⼒穷*举，复杂度⼀般都很⾼。 </p>
<h2 id="回溯之N皇后问题"><a href="#回溯之N皇后问题" class="headerlink" title="回溯之N皇后问题"></a>回溯之N皇后问题</h2><p>给你⼀个 N×N 的棋盘，让你放置 N 个 皇后，使得它们不能互相攻击。 这个问题本质上跟全排列问题差不多，决策树的每⼀层表⽰棋盘上的每⼀ ⾏；每个节点可以做出的选择是，在该⾏的任意⼀列放置⼀个皇后。 </p>
<p>则可以直接套用框架：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">vector</span>&lt;<span class="built_in">vector</span>&lt;<span class="built_in">string</span>&gt;&gt; res; </span><br><span class="line"><span class="comment">/* 输⼊棋盘边⻓ n，返回所有合法的放置 */</span> </span><br><span class="line"><span class="function"><span class="built_in">vector</span>&lt;<span class="built_in">vector</span>&lt;<span class="built_in">string</span>&gt;&gt; <span class="title">solveNQueens</span><span class="params">(<span class="keyword">int</span> n)</span> </span>&#123; </span><br><span class="line">    <span class="comment">// '.' 表⽰空，'Q' 表⽰皇后，初始化空棋盘。 </span></span><br><span class="line">    <span class="function"><span class="built_in">vector</span>&lt;<span class="built_in">string</span>&gt; <span class="title">board</span><span class="params">(n, <span class="built_in">string</span>(n, <span class="string">'.'</span>))</span></span>; </span><br><span class="line">    backtrack(board, <span class="number">0</span>); </span><br><span class="line">    <span class="keyword">return</span> res; </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 路径：board 中⼩于 row 的那些⾏都已经成功放置了皇后 </span></span><br><span class="line"><span class="comment">// 选择列表：第 row ⾏的所有列都是放置皇后的选择</span></span><br><span class="line"><span class="comment">// 结束条件：row 超过 board 的最后⼀⾏ </span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">backtrack</span><span class="params">(<span class="built_in">vector</span>&lt;<span class="built_in">string</span>&gt;&amp; board, <span class="keyword">int</span> row)</span> </span>&#123; </span><br><span class="line">    <span class="comment">// 触发结束条件 </span></span><br><span class="line">    <span class="keyword">if</span> (row == board.<span class="built_in">size</span>()) &#123; </span><br><span class="line">        res.push_back(board); </span><br><span class="line">        <span class="keyword">return</span>; </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">int</span> n = board[row].<span class="built_in">size</span>(); </span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> col = <span class="number">0</span>; col &lt; n; col++) &#123; </span><br><span class="line">        <span class="comment">// 排除不合法选择 </span></span><br><span class="line">        <span class="keyword">if</span> (!<span class="built_in">isValid</span>(board, row, col)) </span><br><span class="line">            <span class="keyword">continue</span>; </span><br><span class="line">        <span class="comment">// 做选择 </span></span><br><span class="line">        board[row][col] = <span class="string">'Q'</span>; </span><br><span class="line">        <span class="comment">// 进⼊下⼀⾏决策 </span></span><br><span class="line">        backtrack(board, row + <span class="number">1</span>); </span><br><span class="line">        <span class="comment">// 撤销选择 </span></span><br><span class="line">        board[row][col] = <span class="string">'.'</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//这部分的主要代码其实跟全排列差不多，都是遍历的思想，先检查是否合理，再进行决策并递归进下一层，下一层递归遍历完之后再撤销选择。</span></span><br><span class="line"><span class="comment">/* 是否可以在 board[row][col] 放置皇后？ */</span> </span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">isValid</span><span class="params">(<span class="built_in">vector</span>&lt;<span class="built_in">string</span>&gt;&amp; board, <span class="keyword">int</span> row, <span class="keyword">int</span> col)</span> </span>&#123; </span><br><span class="line">    <span class="keyword">int</span> n = board.<span class="built_in">size</span>(); </span><br><span class="line">    <span class="comment">// 检查列是否有皇后互相冲突 </span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; n; i++) &#123; </span><br><span class="line">        <span class="keyword">if</span> (board[i][col] == <span class="string">'Q'</span>) </span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>; </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 检查右上⽅是否有皇后互相冲突 </span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = row - <span class="number">1</span>, j = col + <span class="number">1</span>; </span><br><span class="line">         i &gt;= <span class="number">0</span> &amp;&amp; j &lt; n; i--, j++) &#123; </span><br><span class="line">        <span class="keyword">if</span> (board[i][j] == <span class="string">'Q'</span>) </span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>; </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 检查左上⽅是否有皇后互相冲突 </span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = row - <span class="number">1</span>, j = col - <span class="number">1</span>; </span><br><span class="line">         i &gt;= <span class="number">0</span> &amp;&amp; j &gt;= <span class="number">0</span>; i--, j--) &#123; </span><br><span class="line">        <span class="keyword">if</span> (board[i][j] == <span class="string">'Q'</span>) </span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>; </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>函数 backtrack 依然像个在决策树上游⾛的指针，通过 row 和 col 就可 以表⽰函数遍历到的位置，通过 isValid 函数可以将不符合条件的情况剪枝： </p>
<p>这个问题的复杂度确实⾮常⾼，看看我们的决策树，虽 然有 isValid 函数剪枝，但是最坏时间复杂度仍然是 O(N^(N+1))，⽽且⽆ 法优化。如果 N = 10 的时候，计算就已经很耗时了。 <strong>有的时候，我们并不想得到所有合法的答案，只想要⼀个答案，怎么办呢</strong>其实特别简单，只要稍微修改⼀下回溯算法的代码即可：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 函数找到⼀个答案后就返回 </span></span><br><span class="line"><span class="function"><span class="literal">true</span> <span class="keyword">bool</span> <span class="title">backtrack</span><span class="params">(<span class="built_in">vector</span>&lt;<span class="built_in">string</span>&gt;&amp; board, <span class="keyword">int</span> row)</span> </span>&#123; </span><br><span class="line">    <span class="comment">// 触发结束条件 </span></span><br><span class="line">    <span class="keyword">if</span> (row == board.<span class="built_in">size</span>()) &#123; </span><br><span class="line">        res.push_back(board); </span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> col = <span class="number">0</span>; col &lt; n; col++) &#123;</span><br><span class="line">        ... </span><br><span class="line">        board[row][col] = <span class="string">'Q'</span>; </span><br><span class="line">        <span class="keyword">if</span> (backtrack(board, row + <span class="number">1</span>)) </span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>; </span><br><span class="line">        board[row][col] = <span class="string">'.'</span>; </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>回溯算法就是个多叉树的遍历问题，关键就是在前序遍历和后序遍历的位置 做⼀些操作，算法框架如下： <strong>写</strong> <strong>backtrack</strong> <strong>函数时，需要维护⾛过的「路径」和当前可以做的「选择列</strong> <strong>表」，当触发「结束条件」时，将「路径」记⼊结果集</strong></p>
<p>动态规划的三个需要明确的点就是「状态」「选择」和 「base case」，是不是就对应着⾛过的「路径」，当前的「选择列表」和 「结束条件」某种程度上说，动态规划的暴⼒求解阶段就是回溯算法。只是有的问题具有 重叠⼦问题性质，可以⽤ dp table 或者备忘录优化，将递归树⼤幅剪枝，这 就变成了动态规划。⽽今天的两个问题，都没有重叠⼦问题，也就是回溯算 法问题了，复杂度⾮常⾼是不可避免的。 </p>
<h1 id="DFS与BFS详解"><a href="#DFS与BFS详解" class="headerlink" title="DFS与BFS详解"></a>DFS与BFS详解</h1><h2 id="DFS的简要说明"><a href="#DFS的简要说明" class="headerlink" title="DFS的简要说明"></a>DFS的简要说明</h2><p>(1)：深度优先搜索（Depth-First-Search）是搜索算法的一种。是沿着树的深度遍历树的节点，尽可能深的搜索树的分支。当节点v的所有边都己被探寻过，搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点，则选择其中一个作为源节点并重复以上过程，整个进程反复进行直到所有节点都被访问为止。属于盲目搜索。</p>
<p>(2)：DFS是图论里面的一种搜索算法，他可以由一个根节点出发，遍历所有的子节点，进而把图中所有的可以构成树的集合都搜索一遍，达到全局搜索的目的。所以很多问题都可以用dfs来遍历每一种情况，从而得出最优解，但由于时间复杂度太高，我们也叫做<strong>暴力搜索</strong>。</p>
<p>(3)：DFS如同数据结构中的栈结构，是属于一种后进先出的结构，比如说一个羽毛球筒，把它装满之后，我们开始使用时，拿的总是最后放进去的那一个。所以这就导致了所有的点进入栈时有一个顺序，我们称之为 ：<strong>DFS序</strong>。</p>
<p>(4)：根据dfs的英文全写，我们可以知道这是一种深度优先搜索的搜索算法，什么叫做深度优先？意思就是它搜索一颗子树时，它要遍历到底才会 “回头” ，比如说：上有向图中的 搜索模式 为(以DFS序来描述)：a-&gt;b-&gt;e-&gt;b-&gt;f-&gt;c-&gt;f-&gt;b-&gt;c-&gt;b-&gt;a-&gt;c-&gt;a-&gt;d-&gt;g-&gt;d-&gt;a，有人就会问为什么搜索到c的时候不搜索a呢？因为我们假设的这是一个有向图。而且我们可以看到如果 你面对的图是一个 无向图，这个时候这个树将会持续搜索因为可能会形成环路，使得栈的结构一直进进出出，导致程序崩溃，所以我们也因该注意，在写DFS时，如果面对的是一个无向图的话我们需要进行标记。一般的标记方法有:①这个点的父节点被标记，使得子节点不能回到父节点造成循环②访问过的节点被标记。这两种方法视情况而定。</p>
<p>(5)：对于暴搜来说，因其复杂度太高，我们就会想去优化它的复杂度，这一过程称为剪枝，剪纸分为可行性剪枝和最优化剪枝，关于剪枝引申出一种名为记忆化搜索的方法，该方法与动态规划类似。</p>
<h2 id="BFS简要说明"><a href="#BFS简要说明" class="headerlink" title="BFS简要说明"></a>BFS简要说明</h2><p>(1)：与DFS相对的那就是BFS了，BFS称为宽度优先搜索也叫做广度优先搜索，他是按层遍历每一种状态的下一种状态。</p>
<p>搞不懂？没关系我们来看一下一个例题：给你一个整数X，问是否存在一个这样一个数字A：①这个数字是X的倍数②这个数字仅由0与1这两种数字组成 例如 X=2，A=10。这个题当然可以用 DFS来做，我们以1为起点 这样就成了一个 0 1 二叉树，意思就是说每一种 情况 我们都可以 有两种选择 要么 添0，要么在后面 添1。所以我们可以写出代码：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">dfs</span><span class="params">(<span class="keyword">int</span> start)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(A%X==<span class="number">0</span>)<span class="comment">//这是我们搜索结束的条件</span></span><br><span class="line">    &#123;</span><br><span class="line">        ans=A;<span class="comment">//让ans记录答案开始return</span></span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">    dfs(start*<span class="number">10</span>);</span><br><span class="line">    dfs(start*<span class="number">10</span>+<span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>所以我们开始试例子：输入 2按照dfs序的话，首先不符合条件，然后进入下一步搜索，X*10=10，发现10%2==0，然后开始返回ans记录A，这就是最后答案，但是你会发现程序崩溃了。<br>我们分析一下原因：DFS为深度优先搜索，所以我们的左子树可以看作是在末尾加0，然后右子树可以看作在末尾加1，所以这样就形成了一棵树。按照DFS我们的意图是让他搜索左子树，所以在左子树中找到了 满足条件的 数 ：10。但是为什么程序会崩溃呢？</p>
<p>(2）因为搜索到树之后，按照我们刚刚的DFS序，这个树遍开始回溯(你可以想象成这棵树开始回缩)，每回溯到一个节点，因为这个节点的 右子树还没有搜索，所以会再去搜索这个节点的右子树，但是这个节点的右子树是在末尾进行加1，而末尾是1绝对不可能是 2 的倍数 所以这个树会一直按照 往右子树 搜索的趋势 进行下去，导致程序崩溃。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//那是不是这个问题只能枚举了呢？其实DFS是可以的，在网络大神的果断判断之下这个问题出了一个新解：当搜索的深度为19时，此时的答案一定会出现，这时候回溯就好了。所以说我们加一个step记录深度就可以了：</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">dfs</span><span class="params">(<span class="keyword">int</span> start,<span class="keyword">int</span> <span class="built_in">step</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(A%X==<span class="number">0</span>&amp;&amp;<span class="built_in">step</span>==<span class="number">19</span>)<span class="comment">//这是我们搜索结束的条件</span></span><br><span class="line">    &#123;</span><br><span class="line">        ans=A;<span class="comment">//让ans记录答案开始return</span></span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">    dfs(start*<span class="number">10</span>,<span class="built_in">step</span>+<span class="number">1</span>);</span><br><span class="line">    dfs(start*<span class="number">10</span>+<span class="number">1</span>,<span class="built_in">step</span>+<span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>(3)：还有没有更好的方法呢？在考虑这个问题的时候我们回顾一下刚刚为什么程序崩溃，原因就是因为DFS是一个深度优先搜索，他每次必须要 深搜到底 ，所以对于末尾是1来说永远没有结束条件所以它一直会搜索下去！这个时候我们可不可以想有没有这样一种搜索模式，出现A=10这个情况之后我们立刻停止就可以了呢？当然是有的，那就是BFS。</p>
<p>(4)：弄完上面这个例题，也就真正的引出了BFS也发现了DFS的一些小缺点。下面说一下上面留下的定义：<strong>按层遍历每一种的状态的下一种状态</strong>。首先BFS是与数据结构中的 队列 相似，队列是一种 先进先出的结构，这又比如说 你把一些水果按时间一天一个放进一个箱子里，但这些水果时间长了会变质，我们假设变质日期是一样的，那么你每次拿都是拿的最早放进去的那个。</p>
<p>所以说对于刚刚那个题目而言，我们如果按层遍历就不会出现程序崩溃的现象，比如说 第二层有10 11，那我们就不必搜索下一层了</p>
<h2 id="DFS应用"><a href="#DFS应用" class="headerlink" title="DFS应用"></a>DFS应用</h2><p>(1).DFS由于有回溯的步骤，所以我们可以对其进行更新，比如并查集合并时的状态压缩都是使用DFS，还有图论中的 tarjan算法，lca等等。</p>
<p>(2)：搜索 有状态约束的 问题，比如说：N皇后问题；</p>
<h2 id="BFS应用"><a href="#BFS应用" class="headerlink" title="BFS应用"></a>BFS应用</h2><p>(1)：求最短路，这个是图论里面的一种应用被称为SPFA算法，在我之前的博客中有总结过，所以在这里不再提。</p>
<p>(2)：求迷宫最短路，这种题目用DFS也是可以解的，用 DFS的思路:有许多条路可以到达终点我们求一下 到达终点的最小值。这样是非常耗时间的 而BFS不一样因为BFS是按层遍历，所以 每一层对于根节点来说距离一定是最小的，不需要再次更新，我们到达终点时这个距离一定一定是最小的，比如说：</p>
<p>（3)：同时移动性题目问题，火山在喷发，你在跑问是否可以跑出去。详情之前解释过这个问题：</p>
<p>(4)：倒水问题，每次倒水状态之间的转移，之前也有过例题：&amp;&amp;POJ341<a href="https://blog.csdn.net/qq_43857314/article/details/87926470" target="_blank" rel="noopener">pots</a></p>
<p>(5)：路径还原，问从一个点到另一个点 的最短路径是多少，而题目不要求你输入最短距离，而是要求你还原路径这种时候通常需要加一个结构体数组来记录，其实上面的例题pots也是一个路径还原的问题，用一个简单的例题总结一下路径还原：</p>

      
    </div>
    
    
    
    
    <div>
      
    </div>
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2020/09/16/%E7%AE%97%E6%B3%95/%E6%B5%B7%E8%B4%BC%E7%8F%AD%E7%AE%97%E6%B3%95/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/author.jpg">
      <meta itemprop="name" content="Li Yudong">
      <meta itemprop="description" content="">
    </span>
    
    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Technological Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2020/09/16/%E7%AE%97%E6%B3%95/%E6%B5%B7%E8%B4%BC%E7%8F%AD%E7%AE%97%E6%B3%95/" class="post-title-link" itemprop="url">算法入门</a>
        </h2>
    
        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
    
              <time title="创建时间：2020-09-16 18:46:57" itemprop="dateCreated datePublished" datetime="2020-09-16T18:46:57+08:00">2020-09-16</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2021-11-03 16:30:19" itemprop="dateModified" datetime="2021-11-03T16:30:19+08:00">2021-11-03</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E7%AE%97%E6%B3%95/" itemprop="url" rel="index"><span itemprop="name">算法</span></a>
                </span>
            </span>
    
          
    
        </div>
      </header>
    
    
    
    
    <div class="post-body" itemprop="articleBody">
    
      
          <h1 id="leetcode题目"><a href="#leetcode题目" class="headerlink" title="leetcode题目"></a>leetcode题目</h1><h2 id="167-两数之和"><a href="#167-两数之和" class="headerlink" title="167.两数之和"></a>167.两数之和</h2><p>给定一个已按照升序排列 的有序数组，找到两个数使得它们相加之和等于目标数。</p>
<p>函数应该返回这两个下标值 index1 和 index2，其中 index1 必须小于 index2</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> &#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; <span class="title">twoSum</span><span class="params">(<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;&amp; numbers, <span class="keyword">int</span> target)</span> </span>&#123;</span><br><span class="line">        <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; v;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; numbers.<span class="built_in">size</span>(); i++)&#123;</span><br><span class="line">            <span class="keyword">int</span> t = target - numbers[i], l = i + <span class="number">1</span>, r = numbers.<span class="built_in">size</span>() - <span class="number">1</span>;</span><br><span class="line">            <span class="keyword">while</span> (l &lt;= r)&#123;</span><br><span class="line">                <span class="keyword">int</span> mid = (l + r) / <span class="number">2</span>;</span><br><span class="line">                <span class="keyword">if</span> (numbers[mid] == t)&#123;</span><br><span class="line">                    v.push_back(i + <span class="number">1</span>);</span><br><span class="line">                    v.push_back(mid + <span class="number">1</span>);</span><br><span class="line">                    <span class="keyword">return</span> v;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (numbers[mid] &lt; t)&#123;</span><br><span class="line">                    l = mid + <span class="number">1</span>;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    r = mid - <span class="number">1</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> v;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="7、整数反转"><a href="#7、整数反转" class="headerlink" title="7、整数反转"></a>7、整数反转</h2><p>给出一个32位的整数，需要将这个整数中每位上的数字进行反转。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> &#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">reverse</span><span class="params">(<span class="keyword">int</span> x)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">long</span> <span class="keyword">long</span> ans = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span> (x) &#123;</span><br><span class="line">            ans = ans * <span class="number">10</span> + x % <span class="number">10</span>;</span><br><span class="line">            x /= <span class="number">10</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (ans &lt; INT_MIN || ans &gt; INT_MAX)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> (<span class="keyword">int</span>)ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">//小的类型会越界，则添加一个大的类型将其存储，并在后面添加判断语句。</span></span><br></pre></td></tr></table></figure>

<h2 id="9、判断回文数"><a href="#9、判断回文数" class="headerlink" title="9、判断回文数"></a>9、判断回文数</h2><p>判断一个整数是否是回文数；即从右往左和从左往右一样；其实就是将该数反转调过来。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> &#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">bool</span> <span class="title">isPalindrome</span><span class="params">(<span class="keyword">int</span> x)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">long</span> <span class="keyword">long</span> y = <span class="number">0</span>, z = x;</span><br><span class="line">        <span class="keyword">if</span> (z &lt; <span class="number">0</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">while</span> (z)&#123;</span><br><span class="line">            y = y * <span class="number">10</span> + z % <span class="number">10</span>;</span><br><span class="line">            z /= <span class="number">10</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> y == x;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="13、罗马数字转整数"><a href="#13、罗马数字转整数" class="headerlink" title="13、罗马数字转整数"></a>13、罗马数字转整数</h2><h2 id="14、最长公共前缀"><a href="#14、最长公共前缀" class="headerlink" title="14、最长公共前缀"></a>14、最长公共前缀</h2><p>编写一个函数来查找字符串数组中最长公共前缀，不存在则返回空字符串。关键在于用两个for循环，一个用于遍历字符串数组，另一个用于遍历每个字符串中字符。</p>
<p>相当于就是两个for循环来分别遍历字符串和字符串内字符，然后break用于提前退出来节省时间。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Solution</span> &#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="built_in">string</span> <span class="title">longestCommonPrefix</span><span class="params">(<span class="built_in">vector</span>&lt;<span class="built_in">string</span>&gt;&amp; strs)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (strs.<span class="built_in">size</span>() == <span class="number">0</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="string">""</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">string</span> ans = strs[<span class="number">0</span>];</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt; strs.<span class="built_in">size</span>(); i++)&#123;</span><br><span class="line">            <span class="built_in">string</span> t = ans;</span><br><span class="line">            ans = <span class="string">""</span>;</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; t.<span class="built_in">size</span>() &amp;&amp; j &lt; strs[i].<span class="built_in">size</span>(); j++)&#123;</span><br><span class="line">                <span class="keyword">if</span> (t[j] == strs[i][j])&#123;</span><br><span class="line">                    ans += t[j];</span><br><span class="line">                &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (ans == <span class="string">""</span>)&#123;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="20、有效的括号"><a href="#20、有效的括号" class="headerlink" title="20、有效的括号"></a>20、有效的括号</h2><p>完全包含问题则适用于栈来进行处理</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">class Solution &#123;</span><br><span class="line">public:</span><br><span class="line">    bool isValid(string s) &#123;</span><br><span class="line">        stack &lt;char&gt; sta;</span><br><span class="line">        for (int i &#x3D; 0; i &lt; s.size(); i++)&#123;</span><br><span class="line">            if (s[i] &#x3D;&#x3D; &#39;(&#39; || s[i] &#x3D;&#x3D; &#39;[&#39; || s[i] &#x3D;&#x3D; &#39;&#123;&#39;)&#123;</span><br><span class="line">                sta.push(s[i]);</span><br><span class="line">            &#125;</span><br><span class="line">            else&#123;</span><br><span class="line">                if (sta.empty() ||  (s[i] &#x3D;&#x3D; &#39;)&#39; &amp;&amp; sta.top() !&#x3D; &#39;(&#39;) ||</span><br><span class="line">                                    (s[i] &#x3D;&#x3D; &#39;]&#39; &amp;&amp; sta.top() !&#x3D; &#39;[&#39;) ||</span><br><span class="line">                                    (s[i] &#x3D;&#x3D; &#39;&#125;&#39; &amp;&amp; sta.top() !&#x3D; &#39;&#123;&#39;)  )&#123;</span><br><span class="line">                    return false;</span><br><span class="line">                &#125;</span><br><span class="line">                sta.pop();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        return sta.empty();  </span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="21、合并两个有序链表"><a href="#21、合并两个有序链表" class="headerlink" title="21、合并两个有序链表"></a>21、合并两个有序链表</h2><p>将两个升序链表合并成一个新的升序链表并返回，新链表由拼接给定的两个链表的所有节点组成；关键在于链表的操作以及判断边界条件。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Definition for singly-linked list.</span></span><br><span class="line"><span class="comment"> * function ListNode(val, next) &#123;</span></span><br><span class="line"><span class="comment"> *     this.val = (val===undefined ? 0 : val)</span></span><br><span class="line"><span class="comment"> *     this.next = (next===undefined ? null : next)</span></span><br><span class="line"><span class="comment"> * &#125;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;ListNode&#125;</span> <span class="variable">l1</span></span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;ListNode&#125;</span> <span class="variable">l2</span></span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123;ListNode&#125;</span></span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">var</span> mergeTwoLists = <span class="function"><span class="keyword">function</span>(<span class="params">l1, l2</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> ans = <span class="keyword">new</span> ListNode();</span><br><span class="line">    <span class="keyword">var</span> root = ans;</span><br><span class="line">    <span class="keyword">while</span> (l1 != <span class="literal">null</span> || l2 != <span class="literal">null</span>)&#123;</span><br><span class="line">        <span class="keyword">if</span> (l1=== <span class="literal">null</span>)&#123;</span><br><span class="line">            root.next = l2;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (l2 === <span class="literal">null</span>)&#123;</span><br><span class="line">            root.next = l1;</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (l1.val &lt; l2.val)&#123;</span><br><span class="line">            root.next = l1;</span><br><span class="line">            l1 = l1.next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span>&#123;</span><br><span class="line">            root.next = l2;</span><br><span class="line">            l2 = l2.next;</span><br><span class="line">        &#125;</span><br><span class="line">        root = root.next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> ans.next;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="26、删除排序数组重复项"><a href="#26、删除排序数组重复项" class="headerlink" title="26、删除排序数组重复项"></a>26、删除排序数组重复项</h2><p>双指针法</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param <span class="type">&#123;number[]&#125;</span> <span class="variable">nums</span></span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return <span class="type">&#123;number&#125;</span></span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">var</span> removeDuplicates = <span class="function"><span class="keyword">function</span>(<span class="params">nums</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> left = <span class="number">0</span>, right = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">while</span>(right &lt; nums.length)&#123;</span><br><span class="line">        <span class="keyword">if</span>(nums[left] !== nums[right])&#123;</span><br><span class="line">            nums[++left] = nums[right];</span><br><span class="line">        &#125;</span><br><span class="line">        right++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> left + <span class="number">1</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>



<h2 id="27、移除元素"><a href="#27、移除元素" class="headerlink" title="27、移除元素"></a>27、移除元素</h2><p>给你一个数组 nums 和一个值 val，你需要 原地 移除所有数值等于 val 的元素，并返回移除后数组的新长度。</p>
<p>不要使用额外的数组空间，你必须仅使用 O(1) 额外空间并 原地 修改输入数组。</p>
<p>元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> removeElement = <span class="function"><span class="keyword">function</span>(<span class="params">nums, val</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> ind = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; nums.length; i++)&#123;</span><br><span class="line">        <span class="keyword">if</span> (nums[i] != val)&#123;</span><br><span class="line">            nums[ind++] = nums[i];</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> ind;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>


      
    </div>
    
    
    
    
    <div>
      
    </div>
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2020/09/16/%E7%AE%97%E6%B3%95/%E6%9E%81%E5%AE%A2%E7%AE%97%E6%B3%95%E4%B8%93%E6%A0%8F/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/author.jpg">
      <meta itemprop="name" content="Li Yudong">
      <meta itemprop="description" content="">
    </span>
    
    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Technological Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2020/09/16/%E7%AE%97%E6%B3%95/%E6%9E%81%E5%AE%A2%E7%AE%97%E6%B3%95%E4%B8%93%E6%A0%8F/" class="post-title-link" itemprop="url">极客算法专栏</a>
        </h2>
    
        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
    
              <time title="创建时间：2020-09-16 15:53:11" itemprop="dateCreated datePublished" datetime="2020-09-16T15:53:11+08:00">2020-09-16</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2021-11-03 16:30:27" itemprop="dateModified" datetime="2021-11-03T16:30:27+08:00">2021-11-03</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E7%AE%97%E6%B3%95/" itemprop="url" rel="index"><span itemprop="name">算法</span></a>
                </span>
            </span>
    
          
    
        </div>
      </header>
    
    
    
    
    <div class="post-body" itemprop="articleBody">
    
      
          
      
    </div>
    
    
    
    
    <div>
      
    </div>
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2020/09/15/%E7%AE%97%E6%B3%95/%E5%A0%86%E6%8E%92%E5%BA%8F%E5%8F%8A%E5%85%B6%E7%9B%B8%E5%85%B3/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/author.jpg">
      <meta itemprop="name" content="Li Yudong">
      <meta itemprop="description" content="">
    </span>
    
    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Technological Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2020/09/15/%E7%AE%97%E6%B3%95/%E5%A0%86%E6%8E%92%E5%BA%8F%E5%8F%8A%E5%85%B6%E7%9B%B8%E5%85%B3/" class="post-title-link" itemprop="url">堆排序及其相关</a>
        </h2>
    
        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
    
              <time title="创建时间：2020-09-15 21:22:07" itemprop="dateCreated datePublished" datetime="2020-09-15T21:22:07+08:00">2020-09-15</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2021-11-03 16:30:51" itemprop="dateModified" datetime="2021-11-03T16:30:51+08:00">2021-11-03</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E7%AE%97%E6%B3%95/" itemprop="url" rel="index"><span itemprop="name">算法</span></a>
                </span>
            </span>
    
          
    
        </div>
      </header>
    
    
    
    
    <div class="post-body" itemprop="articleBody">
    
      
          
      
    </div>
    
    
    
    
    <div>
      
    </div>
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2020/09/15/%E5%89%8D%E7%AB%AF/css%E4%B8%8Ehtml%E8%AE%BE%E8%AE%A1/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/author.jpg">
      <meta itemprop="name" content="Li Yudong">
      <meta itemprop="description" content="">
    </span>
    
    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Technological Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2020/09/15/%E5%89%8D%E7%AB%AF/css%E4%B8%8Ehtml%E8%AE%BE%E8%AE%A1/" class="post-title-link" itemprop="url">css设计</a>
        </h2>
    
        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
    
              <time title="创建时间：2020-09-15 19:40:46" itemprop="dateCreated datePublished" datetime="2020-09-15T19:40:46+08:00">2020-09-15</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2021-11-03 16:25:21" itemprop="dateModified" datetime="2021-11-03T16:25:21+08:00">2021-11-03</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E5%89%8D%E7%AB%AF%E9%A1%B5%E9%9D%A2/" itemprop="url" rel="index"><span itemprop="name">前端页面</span></a>
                </span>
            </span>
    
          
    
        </div>
      </header>
    
    
    
    
    <div class="post-body" itemprop="articleBody">
    
      
          <h1 id="CSS样式设计"><a href="#CSS样式设计" class="headerlink" title="CSS样式设计"></a>CSS样式设计</h1><h2 id="flex布局语法"><a href="#flex布局语法" class="headerlink" title="flex布局语法"></a>flex布局语法</h2><p>传统的解决方案，基于盒装模型，依赖display 属性 + position属性 + float属性。对于特殊布局非常不方便(比如垂直居中)。flex布局可以简便，完整，响应式地实现各种页面布局，目前已得到所有浏览器的支持。</p>
<h3 id="flex布局概念"><a href="#flex布局概念" class="headerlink" title="flex布局概念"></a>flex布局概念</h3><p>flex 是 Flexible Box 的缩写，意为”弹性布局”，用来为盒装模型提供最大的灵活性。设为 flex 布局以后，子元素的float，clear和vertical-align属性将失效。采用flex布局的元素，称为flex容器，它的所有子元素自动成为容器成员，称为flex项目</p>
<h4 id="轴"><a href="#轴" class="headerlink" title="轴"></a>轴</h4><p>我们知道，轴包括主轴和交叉轴默认情况下，主轴的方向是从左向右的，交叉轴垂直于主轴，逆时针方向90度；交叉轴是由主轴决定的，主轴又是由flex-direction决定的；flex-direction属性设置在父容器上，这样子才可以生效。</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;div class="wrapper"&gt;</span><br><span class="line">        &lt;div class="flex1"&gt;子盒子#flex1: 1 &lt;/div&gt;</span><br><span class="line">        &lt;div class="flex2"&gt;子盒子#flex2: 1 &lt;/div&gt;</span><br><span class="line">&lt;/div&gt;</span><br><span class="line">flex-direction: row | row-reverse | column | column-reverse</span><br></pre></td></tr></table></figure>

<p>当你给父盒子(wrapper)设置属性 flex-direction: row时，flex容器的主轴被定义为与文本方向相同，主轴起点和主轴终点与内容方向相同，简单来说就是主轴沿水平方向向右；</p>
<p>当你给父盒子(wrapper)设置属性 flex-direction: row-reverse时，主轴方向与文本方向相反，因此沿着水平方向向左。</p>
<p>当你给父盒子(wrapper)设置属性 flex-direction: column时，可以看到子盒的布局发生了变化，形成了在Y轴上的布局方式，且书写方式与布局一样。flex容器的主轴和块轴相同，主轴起点与主轴终点和书写模式的前后点相同；主轴变为Y轴方向。</p>
<p>当你给父盒子(wrapper)设置属性 flex-direction: column-reverse；主轴与Y轴方向的书写方向相反。</p>
<h4 id="容器"><a href="#容器" class="headerlink" title="容器"></a>容器</h4><h5 id="父容器"><a href="#父容器" class="headerlink" title="父容器"></a>父容器</h5><p>父容器包括justify-content：设置子元素在主轴方向上的对齐方式。</p>
<p>justify-content：flex-start；子元素沿着主轴方向开始对齐；</p>
<p>justify-content：flex-end；子元素沿着主轴方向终点对齐</p>
<p>justify-content：center；子元素在主轴方向水平居中；</p>
<p>justify-content：space-between；子元素在主轴方向上两端对齐，且项目之间间隔相等；</p>
<p>justify-content：space-around；子元素在主轴方向上均匀排列每个元素，每个元素周围分配相同的空间。</p>
<p>align-items：设置子元素在交叉轴方向上的对齐方式。</p>
<p>align-items: flex-start；子元素在交叉轴方向上起点对齐；</p>
<p>align-items: flex-end；子元素在交叉轴方向上终点对齐；</p>
<p>align-items: center；子元素在交叉轴方向上居中对齐；</p>
<p>align-items: baseline；子元素在交叉轴方向上以文字基线对齐；</p>
<p>align-items: stretch；默认属性，将占满整个容器的高度。</p>
<ul>
<li><strong>flex-wrap</strong>  设置换行方式<ul>
<li>绝对子容器是否可以选择换行，一般而言有三种状态，支持换行的话，也支持逆序换行。</li>
</ul>
</li>
<li><strong>flex-flow</strong> 设置轴向与换行组合<ul>
<li>是 flex-direction 和 flex-wrap 的简写。</li>
<li>所以只要掌握，<code>flex-direction</code> 和 <code>flex-wrap</code>即可。</li>
</ul>
</li>
<li><strong>align-content</strong>  多行沿交叉轴对齐方式<ul>
<li>当子容器多行排列时，设置行与行之间的对齐方式。</li>
</ul>
</li>
</ul>
<h5 id="子容器"><a href="#子容器" class="headerlink" title="子容器"></a>子容器</h5><p>flex属性定义在主轴是如何伸缩的；1、子容器是有弹性的，它们会自动填充剩余空间，子容器的伸缩比由<code>flex</code>属性决定；2、flex是多个属性的缩写，允许1-3个值的连写。</p>
<ul>
<li><strong>flex-grow</strong> 设置扩展比例</li>
<li><strong>flex-shrink</strong> 设置收缩比例</li>
<li><strong>flex-basis</strong> 设置基准大小</li>
<li><strong>order</strong> 设置排列顺序</li>
</ul>
<p><code>align-self</code>属性 单独设置子容器如何沿交叉轴排列；1、每个子容器都可以单独定义沿交叉轴排列方式。2、该属性的取值跟父容器中的align-items属性一致，如果两者相同的话，则以子容器<code>align-self</code>属性为主。</p>
<p>align-self : flex-start；起始端对齐；align-self : flex-end;末尾段对齐；align-self : baseline;基线对齐（第一行文字的基线对齐）；align-self : stretch：拉伸对齐。</p>
<h1 id="SCSS预处理器"><a href="#SCSS预处理器" class="headerlink" title="SCSS预处理器"></a>SCSS预处理器</h1><p>出现原因：1、CSS无法嵌套书写导致代码繁重、冗杂、逻辑混乱；2、没有变量和样式复用机制，属性值只能以字面量的形式重复输出。</p>
<p>sass支持标准的CSS多行注释以及单行注释，前者会被完整输出到编译后的CSS文件中，而后者不会。</p>
<h2 id="变量与数据类型"><a href="#变量与数据类型" class="headerlink" title="变量与数据类型"></a>变量与数据类型</h2><p>变量以美元符号开头，赋值方法与CSS属性的写法一样；</p>
<p>在CSS的样式中，直接使用变量的名称即可调用变量。</p>
<p>作用域：变量支持块级作用域，嵌套规则内定义的变量只能在嵌套规则内使用，不在嵌套规则内定义的变量则可在任何地方使用（全局变量）；将局部变量转换为全局变量可以添加!global声明。</p>
<p>字符串：有引号字符串和无引号字符串。数字：带单位数字与不带单位数字；单位会和数字当做一个整体，进行算数运算。空值：只有一个值null。布尔值。数组。映射。元素。</p>
<h2 id="嵌套"><a href="#嵌套" class="headerlink" title="嵌套"></a>嵌套</h2><p>在创建的样式中经常会有重复的标签样式出现，scss提供了更简单的方法来实现选择器。</p>
<p>使用嵌套时调用父选择器:进行伪类样式的处理，一般会看作被嵌套选择器是父选择器的后代，之后再用hover实现伪类。但有时，我们不想使用该关系时，想直接使用父选择器再加上&amp;,这样后续使用&amp; &amp;-text 等价于.nav .nav-text会自动使用父选择器。</p>
<figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.nav</span> &#123;</span><br><span class="line">    <span class="attribute">height</span>: <span class="number">100px</span>;</span><br><span class="line">    <span class="selector-tag">ul</span> &#123;</span><br><span class="line">        <span class="attribute">margin</span>: <span class="number">0</span>;</span><br><span class="line">        <span class="selector-tag">li</span> &#123;</span><br><span class="line">            <span class="attribute">float</span>: left;</span><br><span class="line">            <span class="attribute">list-style</span>: none;</span><br><span class="line">            <span class="attribute">padding</span>: <span class="number">5px</span></span><br><span class="line">        &#125;</span><br><span class="line">        a &#123;</span><br><span class="line">            display: block;</span><br><span class="line">            <span class="attribute">color</span>: <span class="number">#000</span>;</span><br><span class="line">            <span class="attribute">padding</span>: <span class="number">5px</span>;</span><br><span class="line">            &amp;<span class="selector-pseudo">:hover</span> &#123;</span><br><span class="line">                <span class="attribute">background</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>同样，嵌套除了可以用在样式的规则中，从而减少样式书写中重复的部分，除此之外，样式也可以用于属性中；使用属性的嵌套可以更加简洁。</p>
<figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">body</span> &#123;</span><br><span class="line">    <span class="attribute">font</span>: &#123;</span><br><span class="line">        family: Helvetica, Arial, sans-serif;</span><br><span class="line">        size: <span class="number">15px</span>;</span><br><span class="line">        weight: normal;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.nav</span> &#123;</span><br><span class="line">    <span class="attribute">border</span>: <span class="number">1px</span> solid <span class="number">#000</span>&#123;</span><br><span class="line">        left: <span class="number">0</span>;</span><br><span class="line">        <span class="attribute">right</span>: <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="mxin混合"><a href="#mxin混合" class="headerlink" title="mxin混合"></a>mxin混合</h2><p>mixin有点像是JS中的函数mixin；在SCSS中使用mixin用的是</p>
<figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">@mixin</span> 名字（参数<span class="number">1</span>， 参数<span class="number">2</span>...）&#123;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">@mixin</span> alert(<span class="variable">$text-color</span>, <span class="variable">$background</span>) &#123;</span><br><span class="line">    <span class="attribute">color</span>: <span class="variable">$text-color</span>;</span><br><span class="line">    back-ground-<span class="attribute">color</span>: <span class="variable">$background</span>;</span><br><span class="line">    <span class="selector-tag">a</span> &#123;</span><br><span class="line">        <span class="attribute">color</span>: darken(<span class="variable">$text-color</span>, <span class="number">10%</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.alert-warning</span> &#123;</span><br><span class="line">    <span class="keyword">@include</span> alert(<span class="number">#8a6d3b</span>, <span class="number">#fcf8e3</span>);</span><br><span class="line">    <span class="comment">//@include来使用alert定义的样式</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="extend继承扩展"><a href="#extend继承扩展" class="headerlink" title="@extend继承扩展"></a>@extend继承扩展</h2><p>用一个选择器去继承另一个选择器中定义的所有样式，避免重复的样式编写。这种继承同时会继承alert里面与其相关的内部嵌套选择器的样式.</p>
<figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">@import</span> <span class="string">"base"</span>;<span class="comment">//输入时则不用加上前面的下划线</span></span><br><span class="line">    </span><br><span class="line"><span class="selector-class">.alert</span> &#123;</span><br><span class="line">    <span class="attribute">padding</span>: <span class="number">15px</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.alert</span> <span class="selector-tag">a</span> &#123;</span><br><span class="line">    <span class="attribute">font-weight</span>: bold;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.alert-info</span> &#123;</span><br><span class="line">    <span class="keyword">@extend</span> .alert;</span><br><span class="line">    <span class="attribute">background-color</span>: </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="import"><a href="#import" class="headerlink" title="@import"></a>@import</h2><p>便于将一个项目需要的样式分割成不同的小的部分Partials，项目名以下划线开头来提醒其为小部分，浏览器不会去单独编译其为CSS。</p>
<h2 id="数值与字符串"><a href="#数值与字符串" class="headerlink" title="数值与字符串"></a>数值与字符串</h2><p>SCSS中数值均会包含单位；且提供了一些数字函数来更方便地处理数字。</p>
<p>abs()：返回输入参数的绝对值；round(3.5)：四舍五入函数；ceil(3.2)：进位函数；floor(3.6)：退位函数；percentage(650px / 1000px)：变为百分数；min、max函数</p>
<p>关键字、带引号字符串、不带引号字符串；</p>
<p>+可以将两个字符串连接到一块，返回的结果会自带引号；字符串+数字=》字符串；</p>
<p>字符串函数：to-upper-case：变大写；to-lower=-case：变小写；str-length:返回长度</p>
<h2 id="颜色"><a href="#颜色" class="headerlink" title="颜色"></a>颜色</h2><p>rgb函数：rgb(255, 100, 0)橙色;rgba函数：可以使颜色拥有透明度的设置，0~1，(255, 255, 0, 0.8);</p>
<p>HSL函数（色相、饱和度、明度）：hsla(60, 100%, 50%, 0.5);</p>
<p>adjust-hue函数，调整色相的值，adjust-hue($base-color-hsl, 137deg)；</p>
<p>lighten和darken函数：改变函数的明暗程度，lighten($base-color, 30%);</p>
<p>saturate、desaturate:增加或减少颜色的纯度，即饱和度，saturate($base-color, 50%);</p>
<p>transparentize、opacify：增加与</p>

      
    </div>
    
    
    
    
    <div>
      
    </div>
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2020/09/09/%E5%89%8D%E7%AB%AF/TypeScript%E5%85%A5%E9%97%A8/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/author.jpg">
      <meta itemprop="name" content="Li Yudong">
      <meta itemprop="description" content="">
    </span>
    
    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Technological Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2020/09/09/%E5%89%8D%E7%AB%AF/TypeScript%E5%85%A5%E9%97%A8/" class="post-title-link" itemprop="url">TypeScript入门</a>
        </h2>
    
        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
    
              <time title="创建时间：2020-09-09 20:02:33" itemprop="dateCreated datePublished" datetime="2020-09-09T20:02:33+08:00">2020-09-09</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2021-11-03 16:26:40" itemprop="dateModified" datetime="2021-11-03T16:26:40+08:00">2021-11-03</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E5%89%8D%E7%AB%AF%E9%A1%B5%E9%9D%A2/" itemprop="url" rel="index"><span itemprop="name">前端页面</span></a>
                </span>
            </span>
    
          
    
        </div>
      </header>
    
    
    
    
    <div class="post-body" itemprop="articleBody">
    
      
          <h1 id="TypeScript与JS关系"><a href="#TypeScript与JS关系" class="headerlink" title="TypeScript与JS关系"></a>TypeScript与JS关系</h1><p>JS是一个动态类型语言，即类型不固定，写起来灵活、自由但后期不太好维护，且出错之后不好去管理与排查。TS便是用来解决JS这方面的问题，在JS基础上加了一些类型特性，类型约束+自动补全+智能提示。</p>
<p>类型系统带来什么：1、接口；2、重载；3、泛型。</p>
<p>TS是为了解决JS在编码过程中，在类型的使用错误问题；解决的方法有很多。关注点不在于增加什么新功能，而是仅仅扩大JS的类型检测、类型设定。</p>
<p>index.d.ts：类型声明文件，</p>

      
    </div>
    
    
    
    
    <div>
      
    </div>
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2020/09/07/%E5%89%8D%E7%AB%AF/ES6%E6%96%B0%E5%A2%9E%E6%A0%87%E5%87%86/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/author.jpg">
      <meta itemprop="name" content="Li Yudong">
      <meta itemprop="description" content="">
    </span>
    
    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Technological Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2020/09/07/%E5%89%8D%E7%AB%AF/ES6%E6%96%B0%E5%A2%9E%E6%A0%87%E5%87%86/" class="post-title-link" itemprop="url">ES6新增标准</a>
        </h2>
    
        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
    
              <time title="创建时间：2020-09-07 17:39:59" itemprop="dateCreated datePublished" datetime="2020-09-07T17:39:59+08:00">2020-09-07</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2021-11-03 16:25:29" itemprop="dateModified" datetime="2021-11-03T16:25:29+08:00">2021-11-03</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E5%89%8D%E7%AB%AF%E9%A1%B5%E9%9D%A2/" itemprop="url" rel="index"><span itemprop="name">前端页面</span></a>
                </span>
            </span>
    
          
    
        </div>
      </header>
    
    
    
    
    <div class="post-body" itemprop="articleBody">
    
      
          <p>三大组成部分：ECMAScript；DOM（文档对象模型）：对文档的操作；BOM（浏览器对象模型）：主要包括对浏览器的相关操作；</p>
<h1 id="let和const"><a href="#let和const" class="headerlink" title="let和const"></a>let和const</h1><h2 id="let、const和var的区别"><a href="#let、const和var的区别" class="headerlink" title="let、const和var的区别"></a>let、const和var的区别</h2><h3 id="var的特性"><a href="#var的特性" class="headerlink" title="var的特性"></a>var的特性</h3><p>1、var可以重复声明；</p>
<p>2、var作用域：全局作用域和函数作用域；</p>
<p>3、会进行预解析，但可能造成代码混乱；</p>
<h3 id="let的特性"><a href="#let的特性" class="headerlink" title="let的特性"></a>let的特性</h3><p>1、let在同一作用域下不能进行重复声明；</p>
<p>2、let作用域：全局作用域和块级作用域；if、while、for等有{}的语句，let会考虑在花括号之间的为块级作用域，仅在代码块内部起作用；（与C++类似）</p>
<p>3、不进行预解析；JS有预解析机制，在下面声明而在上面去调用并不会报错，然而使用let声明时不会进行预解析；</p>
<h3 id="const的特性"><a href="#const的特性" class="headerlink" title="const的特性"></a>const的特性</h3><p>1、const为常量，声明时必须赋值，而之后并不能对其进行修改；</p>
<p>2、其他情况与let一样，不能重复声明、不能预解析、为块级作用域。</p>
<h1 id="块级作用域"><a href="#块级作用域" class="headerlink" title="块级作用域"></a>块级作用域</h1><p>之前在写代码时，为了避免与全局冲突，一般会先声明一个匿名函数，并马上对其调用；</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    </span><br><span class="line">&#125;)()</span><br></pre></td></tr></table></figure>

<p>有了块级作用域之后，可以直接用{}来生成代码块，便能直接在代码块里添加代码；</p>
<p>如果在循环之中添加了一个块级作用域，将要如何去处理，</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="keyword">let</span> lis = <span class="built_in">document</span>.querySelectorAll(<span class="string">"li"</span>);</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; lis.length; i++)&#123;</span><br><span class="line">        lis[i].onclick = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">            <span class="built_in">console</span>.log(i);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//这么写相当于生成了lis.length个代码块，遍历i时每个i的值均声成代码块，循环几次便开辟几个块级作用域；</span></span><br></pre></td></tr></table></figure>

<h1 id="解构赋值"><a href="#解构赋值" class="headerlink" title="解构赋值"></a>解构赋值</h1><h2 id="对象的解构赋值"><a href="#对象的解构赋值" class="headerlink" title="对象的解构赋值"></a>对象的解构赋值</h2><p>对象可以有若干属性，且每个属性方法衷都可以存储数据；当你希望用其他的变量将原变量的属性存储起来，原本的JS写法如下：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> obj = &#123;</span><br><span class="line">    a: <span class="number">1</span>,</span><br><span class="line">    b: <span class="number">2</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">let</span> a = obj.a;</span><br><span class="line"><span class="keyword">let</span> b = obj.b;</span><br></pre></td></tr></table></figure>

<p>在ES6的标准下，可以把声明改成这样：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> obj = &#123;</span><br><span class="line">    a: <span class="number">1</span>,</span><br><span class="line">    b: <span class="number">2</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">let</span> &#123;a, b, c&#125; = obj;</span><br><span class="line"><span class="comment">//语法中对象的名字必须与obj里面的属性名称一样，这样才能进行对象解构赋值，故c为undefined</span></span><br></pre></td></tr></table></figure>

<h2 id="数组的解构赋值"><a href="#数组的解构赋值" class="headerlink" title="数组的解构赋值"></a>数组的解构赋值</h2><p>对象的解构赋值要求变量名与属性名一致，但数组的话，仅要求顺序必须一致；</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = [<span class="string">"a"</span>, <span class="string">"b"</span>, <span class="string">"c"</span>];</span><br><span class="line"><span class="keyword">let</span> [e, f] = arr;</span><br><span class="line"></span><br><span class="line"><span class="comment">//面试题：如何快速交换a, b值</span></span><br><span class="line"><span class="keyword">let</span> a = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">let</span> b = <span class="number">1</span>;</span><br><span class="line">[a, b] = [b, a];</span><br><span class="line"><span class="comment">//使用数组解构赋值便能快速交换；</span></span><br></pre></td></tr></table></figure>

<h2 id="字符串的解构赋值"><a href="#字符串的解构赋值" class="headerlink" title="字符串的解构赋值"></a>字符串的解构赋值</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> str = <span class="string">"abc"</span>;</span><br><span class="line"><span class="keyword">let</span> [e, f] = str</span><br><span class="line"><span class="comment">//其实跟数组一样，根据顺序的索引来解构赋值</span></span><br></pre></td></tr></table></figure>

<h1 id="展开运算符"><a href="#展开运算符" class="headerlink" title="展开运算符"></a>展开运算符</h1><h2 id="数组展开"><a href="#数组展开" class="headerlink" title="数组展开"></a>数组展开</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>];</span><br><span class="line"><span class="keyword">let</span> arr2 = [<span class="string">"a"</span>, <span class="string">"b"</span>,...arr,<span class="string">"c"</span>, <span class="string">"d"</span>];</span><br><span class="line"><span class="comment">//如何简单处理，将两个数组加一起</span></span><br><span class="line"><span class="comment">//剩余参数</span></span><br><span class="line"><span class="keyword">let</span> [a, b,...c] = arr;</span><br></pre></td></tr></table></figure>

<h2 id="对象展开"><a href="#对象展开" class="headerlink" title="对象展开"></a>对象展开</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> obj = &#123;</span><br><span class="line">    a:<span class="number">1</span>,</span><br><span class="line">    b:<span class="number">2</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">let</span> obj2 = &#123;</span><br><span class="line">    ...obj,</span><br><span class="line">    c:<span class="number">3</span>,</span><br><span class="line">    d:<span class="number">4</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">//不建议对象直接赋给另一个对象，不然一个改变另一个也会改变，因为其本身传递的是一个地址，那么如何来处理呢，用obj解构来代替obj，其本质是所有内容而不是对象的地址</span></span><br><span class="line"><span class="keyword">let</span> obj2 = &#123;...obj&#125;;</span><br></pre></td></tr></table></figure>

<h1 id="set对象"><a href="#set对象" class="headerlink" title="set对象"></a>set对象</h1><p>set本身是一个函数，用于构建对象，还有Data、Array,调用后返回构造对象，统称构造函数，用于构造某一类型的对象，即对象的实例化。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>]</span><br><span class="line"><span class="keyword">let</span>  s = <span class="keyword">new</span> <span class="built_in">Set</span>(arr);</span><br><span class="line">arr = [...s]</span><br><span class="line"><span class="comment">//arr返回之后便是去重之后的结果</span></span><br><span class="line">s.size<span class="comment">//size属性，储存保留之后值得个数</span></span><br><span class="line">s.clear()；<span class="comment">//清空</span></span><br><span class="line">s.delete(<span class="string">"a"</span>);<span class="comment">//删除某项值</span></span><br><span class="line">s.add(<span class="number">6</span>);<span class="comment">//添加某项值</span></span><br><span class="line">s.has();<span class="comment">//查看是否包含某个值</span></span><br></pre></td></tr></table></figure>

<h1 id="Map对象"><a href="#Map对象" class="headerlink" title="Map对象"></a>Map对象</h1><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = [</span><br><span class="line">    [<span class="string">"a"</span>,<span class="number">1</span>],</span><br><span class="line">    [<span class="string">"b"</span>,<span class="number">2</span>],</span><br><span class="line">    [<span class="string">"c"</span>,<span class="number">3</span>]</span><br><span class="line">]；</span><br><span class="line"><span class="keyword">let</span> s = <span class="keyword">new</span> <span class="built_in">Map</span>(arr);</span><br><span class="line"><span class="comment">//map结构会存成键值对得形式key-value</span></span><br><span class="line">clear();<span class="comment">//清空所有值</span></span><br><span class="line"><span class="keyword">delete</span>();<span class="comment">//删除某一项</span></span><br><span class="line"><span class="comment">//参数：key，数据的key值；返回值，true、false是否删除成功</span></span><br><span class="line"><span class="keyword">get</span>();//获取某一项值</span><br><span class="line">has();//是否包含某一项</span><br><span class="line"><span class="keyword">set</span>(key, val);//设置一个值</span><br></pre></td></tr></table></figure>

<h1 id="函数新增内容"><a href="#函数新增内容" class="headerlink" title="函数新增内容"></a>函数新增内容</h1><h2 id="箭头函数"><a href="#箭头函数" class="headerlink" title="箭头函数"></a>箭头函数</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//function()&#123;return ;&#125;</span></span><br><span class="line"><span class="keyword">let</span> fn = <span class="function"><span class="params">()</span>=&gt;</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="number">1</span>);</span><br><span class="line">&#125;;</span><br><span class="line">fn();</span><br><span class="line"><span class="comment">//箭头函数，=&gt;</span></span><br><span class="line"><span class="comment">//形参=&gt;返回值</span></span><br><span class="line"><span class="keyword">let</span> fn = <span class="function"><span class="params">nub</span> =&gt;</span> nub *<span class="number">2</span>;</span><br><span class="line"><span class="comment">//多个形参时，则加括号（形参，形参）=&gt;返回值</span></span><br><span class="line"><span class="comment">//()=&gt;返回值,没有参数时也要加上括号</span></span><br><span class="line"><span class="comment">//  ()=&gt;&#123;</span></span><br><span class="line"><span class="comment">//  执行语句</span></span><br><span class="line"><span class="comment">//   return 返回值</span></span><br><span class="line"><span class="comment">//    &#125;</span></span><br><span class="line"><span class="comment">//箭头函数没有arguments，即不能使用不定参,但可以使用扩展运算符加剩余参数来进行实现不定参数的功能,</span></span><br><span class="line"><span class="keyword">let</span> fn = <span class="function">(<span class="params">a,b...arg</span>)=&gt;</span></span><br><span class="line"><span class="comment">//rest参数存在数组中，即拿到该数组便能拿到剩余参数，其实跟前面的展开运算符一致</span></span><br></pre></td></tr></table></figure>



<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">document</span>,onclick = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">let</span> fn = <span class="function"><span class="params">()</span>=&gt;</span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="keyword">this</span>);</span><br><span class="line">    &#125;;</span><br><span class="line">    fn();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>使用正常函数时，this指针一般指向windows，但换成箭头函数时，this指针指向document；因为箭头函数本身没有this，调用箭头函数的this时，指向的是其声明时所在的作用域的this；</p>
<p>参数默认值问题:ES6中有简便方法设置参数默认值</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> fn = <span class="function">(<span class="params">a = <span class="number">2</span>,b = <span class="number">10</span></span>)=&gt;</span>&#123;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h1 id="数组新增方法"><a href="#数组新增方法" class="headerlink" title="数组新增方法"></a>数组新增方法</h1><h2 id="日期对象"><a href="#日期对象" class="headerlink" title="日期对象"></a>日期对象</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> <span class="built_in">Date</span>().getTime();<span class="comment">//方法属于Date返回的对象的方法,需要对对象进行调用</span></span><br><span class="line"><span class="built_in">Date</span>.now();<span class="comment">//直接使用Date的方法；</span></span><br><span class="line"><span class="keyword">new</span> <span class="built_in">Array</span>()</span><br><span class="line"><span class="built_in">Array</span>.from();</span><br><span class="line">arr.forEach();<span class="comment">//需要区分一下哪些方法是构造函数的本身方法，而哪些方法是需要利用对象来调用的方法</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//新增方法如下</span></span><br><span class="line"><span class="built_in">Array</span>.from()<span class="comment">//把一个类数组转换成真正的数组，类数组：有下标有length；返回值：转换之后的新数组；是Array构造函数的方法</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">let</span> lis = <span class="built_in">document</span>.querySelectorAll(<span class="string">"#list li"</span>);</span><br><span class="line">    <span class="comment">//map是数组方法，因此需要先将lis从类数组变成数组</span></span><br><span class="line">    <span class="keyword">let</span> arr=[];</span><br><span class="line">    lis = <span class="built_in">Array</span>.from(lis);</span><br><span class="line">    lis.map(<span class="function"><span class="params">item</span>=&gt;</span>&#123;</span><br><span class="line">       <span class="keyword">return</span> item; </span><br><span class="line">    &#125;);</span><br><span class="line">    lis = <span class="built_in">Array</span>.from(lis,(item,index)=&gt;&#123;</span><br><span class="line">       <span class="built_in">console</span>.log(item, index);</span><br><span class="line">        <span class="keyword">return</span> index;</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//利用from组成新数组</span></span><br><span class="line"><span class="comment">//of方法将放入的元素组成新数组后返回</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">Array</span>.of(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="string">"5"</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//isArray方法：判断接收的数据是否是数组，类数组不是数组</span></span><br></pre></td></tr></table></figure>

<h2 id="数组的find、findIndex"><a href="#数组的find、findIndex" class="headerlink" title="数组的find、findIndex"></a>数组的find、findIndex</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="comment">//find查找数组中满足要求的第一个元素的值</span></span><br><span class="line">    <span class="keyword">let</span> arr = [<span class="string">"a"</span>, <span class="string">"b"</span>, <span class="string">"c"</span>, <span class="string">"d"</span>];</span><br><span class="line">    arr.indexOf(<span class="string">"a"</span>);</span><br><span class="line">    <span class="keyword">let</span> arr = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>];</span><br><span class="line">    <span class="keyword">let</span> val = arr.find(<span class="function">(<span class="params">item,index</span>)=&gt;</span>&#123;</span><br><span class="line">       <span class="keyword">if</span>(item &gt; <span class="number">3</span>)&#123;</span><br><span class="line">           <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">       &#125; </span><br><span class="line">    &#125;);</span><br><span class="line">    val = arr.find(<span class="function"><span class="params">item</span>=&gt;</span> item &gt;= <span class="number">3</span>);</span><br><span class="line">    <span class="built_in">console</span>.log(val);</span><br><span class="line">    <span class="comment">//fingIndex返回数组中满足要求的第一个元素的索引</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="数组扁平化处理"><a href="#数组扁平化处理" class="headerlink" title="数组扁平化处理"></a>数组扁平化处理</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = &#123;</span><br><span class="line">    [<span class="string">"小明"</span>，<span class="string">"18"</span>],</span><br><span class="line">    [<span class="string">"小刚"</span>，<span class="string">"18"</span>],</span><br><span class="line">&#125;</span><br><span class="line">arr.flat(n);</span><br><span class="line"><span class="comment">//flat(n)将多维数组转换成低维的数组，向下提取n层;后续传递的参数决定了提取几层</span></span><br><span class="line"><span class="comment">//当嵌套多层时，使用</span></span><br><span class="line">flat(<span class="literal">Infinity</span>);<span class="comment">//提取无限层，确保变为一维数组</span></span><br><span class="line"><span class="comment">//利用flatMap来扁平化处理后得到每一项</span></span><br><span class="line"><span class="keyword">let</span> newArr = arr.flatMap(<span class="function">(<span class="params">item,index</span>)=&gt;</span>&#123;</span><br><span class="line">   cosole.log(item,index);</span><br><span class="line">    item = item.filter(<span class="function">(<span class="params">item,index</span>)=&gt;</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> index == <span class="number">0</span>;</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">return</span> item;</span><br><span class="line">&#125;);</span><br><span class="line"><span class="built_in">console</span>.log(newArr);</span><br></pre></td></tr></table></figure>

<p>flatMap参数：callback回调函数，可以生成一个新数组中元素的参数；可选参数：thisArg，执行callback函数时，使用的this值；返回值：一个包含将数组和子数组中所有元素的数组。</p>
<p>flatMap：只能处理一层的数组，要处理多层的话需要进行if判断是否里面还存在数组，并进行递归操作进行多次扁平化。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//fill语句，将数组用...数据填充满，从第几位开始填充，且本身是不修改原数组长度的</span></span><br><span class="line"><span class="comment">//includes判断数组中是否包含一个指定的值</span></span><br><span class="line"><span class="keyword">let</span> arr = [<span class="string">"a"</span>,<span class="string">"b"</span>,<span class="string">"c"</span>,<span class="string">"d"</span>,<span class="string">"e"</span>];</span><br><span class="line">arr.includes(<span class="string">"c"</span>,<span class="number">2</span>);</span><br><span class="line"><span class="comment">//后面参数代表从第几位开始检索</span></span><br></pre></td></tr></table></figure>

<h1 id="字符串新增方法"><a href="#字符串新增方法" class="headerlink" title="字符串新增方法"></a>字符串新增方法</h1><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> str = <span class="string">"开课吧和面为课堂"</span>；</span><br><span class="line">str.startsWith(<span class="string">"开课吧"</span>);<span class="comment">//返回一个bool值</span></span><br><span class="line">str.endsWith(<span class="string">"面为课堂"</span>)；</span><br><span class="line">str.repeat(<span class="number">30</span>);</span><br><span class="line"><span class="comment">//模板字符串，如何快速地构造出想要的字符串</span></span><br><span class="line"><span class="comment">//$&#123;&#125;插值表达式，可以用值，也可以用函数，适合该值有复杂的逻辑处理</span></span><br><span class="line"><span class="keyword">let</span> p = <span class="built_in">document</span>.querySelector(<span class="string">"p"</span>);</span><br><span class="line"><span class="keyword">let</span> name = <span class="string">"小明"</span>;</span><br><span class="line"><span class="keyword">let</span> age = <span class="number">18</span>;</span><br><span class="line"><span class="keyword">let</span> school = <span class="string">"大学"</span>;</span><br><span class="line">p.innerHTML = <span class="string">`今年&lt;strong&gt;<span class="subst">$&#123;name&#125;</span>&lt;/strong&gt;就要&lt;strong&gt;<span class="subst">$&#123;age&#125;</span>&lt;/strong&gt;岁了，终于升入&lt;strong&gt;<span class="subst">$&#123;school&#125;</span>&lt;/strong&gt;了`</span>；</span><br><span class="line"><span class="comment">//先用``反引号将整个值包起来，在用插值表达式来一个个插入值</span></span><br><span class="line"><span class="comment">//模板字符串可以换行</span></span><br></pre></td></tr></table></figure>

<h1 id="对象新增方法"><a href="#对象新增方法" class="headerlink" title="对象新增方法"></a>对象新增方法</h1><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> a = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">let</span> b = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">let</span> name = <span class="string">"小明"</span>;</span><br><span class="line"><span class="keyword">let</span> obj = &#123;</span><br><span class="line">    a,</span><br><span class="line">    b,</span><br><span class="line">    c()&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">"a"</span>);</span><br><span class="line">    &#125;,</span><br><span class="line">    [name]: <span class="number">111</span>;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">//可以直接在obj里面起属性名表达式，可通过变量来给属性名赋值。</span></span><br><span class="line"><span class="comment">//对象合并,其实可以使用之前的对象展开来进行合并</span></span><br><span class="line"><span class="keyword">let</span> obj = &#123;</span><br><span class="line">    a: <span class="number">1</span>,</span><br><span class="line">    b :<span class="number">2</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">let</span> obj2 = &#123;</span><br><span class="line">    c : <span class="number">3</span>,</span><br><span class="line">    d :<span class="number">4</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="built_in">Object</span>.assign(obj2 ，obj);<span class="comment">//第一个参数为合并传入的对象</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//is方法 obj函数下的方法，接受两个值并判断是否一样</span></span><br></pre></td></tr></table></figure>

<h1 id="Babel"><a href="#Babel" class="headerlink" title="Babel"></a>Babel</h1><p>babel是一个JavaScript编译器，用于语法编译，把JS本身不识别、不兼容的语法糖编译成兼容的，babel最简单的使用：将babel引入到页面当中。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> a = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">let</span> b = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">let</span> obj = &#123;</span><br><span class="line">    a,</span><br><span class="line">    b,</span><br><span class="line">    c()&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">let</span> obj2 = &#123;</span><br><span class="line">    d: <span class="number">4</span>,</span><br><span class="line">    ...obj,</span><br><span class="line">    e: <span class="number">5</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h1 id="Promise"><a href="#Promise" class="headerlink" title="Promise"></a>Promise</h1><p>Promise是异步编程的一种解决方案，比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现，ES6 将其写进了语言标准，统一了用法，原生提供了<code>Promise</code>对象。</p>
<p>所谓<code>Promise</code>，简单说就是一个容器，里面保存着某个未来才会结束的事件（通常是一个异步操作）的结果。从语法上说，Promise 是一个对象，从它可以获取异步操作的消息。Promise 提供统一的 API，各种异步操作都可以用同样的方法进行处理。</p>
<p><code>Promise</code>对象有以下两个特点：</p>
<p>1、对象的状态不受外界影响。<code>Promise</code>对象代表一个异步操作，有三种状态：<code>pending</code>（进行中）、<code>fulfilled</code>（已成功）和<code>rejected</code>（已失败）。只有异步操作的结果，可以决定当前是哪一种状态，任何其他操作都无法改变这个状态。这也是<code>Promise</code>这个名字的由来，它的英语意思就是“承诺”，表示其他手段无法改变；</p>
<p>2、一旦状态改变，就不会再变，任何时候都可以得到这个结果。<code>Promise</code>对象的状态改变，只有两种可能：从<code>pending</code>变为<code>fulfilled</code>和从<code>pending</code>变为<code>rejected</code>。只要这两种情况发生，状态就凝固了，不会再变了，会一直保持这个结果，这时就称为 resolved（已定型）。如果改变已经发生了，你再对<code>Promise</code>对象添加回调函数，也会立即得到这个结果。这与事件（Event）完全不同，事件的特点是，如果你错过了它，再去监听，是得不到结果的。</p>
<p>有了<code>Promise</code>对象，就可以将异步操作以同步操作的流程表达出来，避免了层层嵌套的回调函数。此外，<code>Promise</code>对象提供统一的接口，使得控制异步操作更加容易。缺点：无法取消<code>Promise</code>，一旦新建它就会立即执行，无法中途取消。其次，如果不设置回调函数，<code>Promise</code>内部抛出的错误，不会反应到外部。第三，当处于<code>pending</code>状态时，无法得知目前进展到哪一个阶段（刚刚开始还是即将完成）。</p>
<h2 id="回调函数"><a href="#回调函数" class="headerlink" title="回调函数"></a>回调函数</h2><p>一个通过函数指针调用的函数。如果你把函数的指针（地址）作为参数传递给另一个函数，当这个指针被用来调用其所指向的函数时，我们就说这是回调函数。回调函数不是由该函数的实现方直接调用，而是在特定的事件或条件发生时由另外的一方调用的，用于对该事件或条件进行响应。</p>
<p>其实回调就是一种利用函数指针进行函数调用的过程.</p>
<p>为什么要用回调呢?比如我要写一个子模块给你用,来接收远程socket发来的命令.当我接收到命令后,需要调用你的主模块的函数, 来进行相应的处理.但是我不知道你要用哪个函数来处理这个命令,我也不知道你的主模块是什么.cpp或者.h,或者说,我根本不用关心你在主模块里怎么处理它,也不应该关心用什么函数处理它……怎么办呢？使用回调!</p>
<p><strong><code>使用回调函数实际上就是在调用某个函数（通常是API函数）时，将自己写的一个函数（这个函数就是回调函数）的地址作为参数传递给那个函数。回调其实就是提供使用某模块的一种方法。回调函数就好比是一个中断处理函数。</code></strong></p>
<p>在解释这种思想前我想先说明一下，回调函数固然能解决一部分系统架构问题但是绝不能再系统内到处都是，如果你发现你的系统内到处都是回调函数，那么你一定要重构你的系统。回调函数本身是一种破坏系统结构的设计思路，回调函数会绝对的变化系统的运行轨迹，执行顺序，调用顺序。回调函数的出现会让读到你的代码的人非常的懵头转向。</p>
<p>既然我们需要模块间的协作，同时我们又厌恶的摒弃模块间你中有我我中有你的暧昧关系那如何生成系统呢，答案是函数指针（不一定一定是函数指针）也就是使用回调的方式。如果一个对象关心另一个对象的状态变化那么给状态的变化注册回调函数让它通知你这类状态的改变，这样在封装了模块变化的同时实现了模块间的协作关系另辟独径的给对象解耦。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> <span class="keyword">async</span>=<span class="function"><span class="keyword">function</span>(<span class="params">callback</span>)</span>&#123;</span><br><span class="line">    setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;    <span class="comment">//1秒后回调</span></span><br><span class="line">        callback(<span class="string">'data'</span>);</span><br><span class="line">    &#125;,<span class="number">1000</span>);</span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">async</span>(<span class="function"><span class="keyword">function</span>(<span class="params">data</span>)</span>&#123;</span><br><span class="line">    alert(data);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> friends = [<span class="string">"Mike"</span>, <span class="string">"Stacy"</span>, <span class="string">"Andy"</span>, <span class="string">"Rick"</span>];</span><br><span class="line">friends.forEach(<span class="function"><span class="keyword">function</span> (<span class="params">eachName, index</span>)</span>&#123;</span><br><span class="line"><span class="built_in">console</span>.log(index + <span class="number">1</span> + <span class="string">". "</span> + eachName); <span class="comment">// 1. Mike, 2. Stacy, 3. Andy, 4. Rick</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>



<p>需要注意的很重要的一点是回调函数并不会马上被执行。它会在包含它的函数内的某个特定时间点被“回调”（就像它的名字一样）。因此，即使第一个jQuery的例子如下所示：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//匿名函数不会再参数中被执行</span></span><br><span class="line"><span class="comment">//这是一个回调函数    </span></span><br><span class="line">$(<span class="string">"#btn_1"</span>).click(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    alert(<span class="string">"Btn 1 Clicked"</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>这个回调函数在包含它的函数内的某一点执行，就好像这个回调函数是在包含它的函数中定义的一样。这意味着回调函数本质上是一个闭包。正如我们所知，闭包能够进入包含它的函数的作用域，因此回调函数能获取包含它的函数中的变量，以及全局作用域中的变量。</p>
<h3 id="回调函数基本原理"><a href="#回调函数基本原理" class="headerlink" title="回调函数基本原理"></a>回调函数基本原理</h3><h4 id="命名或匿名函数作为回调"><a href="#命名或匿名函数作为回调" class="headerlink" title="命名或匿名函数作为回调"></a>命名或匿名函数作为回调</h4><p>在前面的jQuery例子以及forEach的例子中，我们使用了再参数位置定义的匿名函数作为回调函数。这是在回调函数使用中的一种普遍的魔术。另一种常见的模式是定义一个命名函数并将函数名作为变量传递给函数。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//全局变量</span></span><br><span class="line"><span class="keyword">var</span> allUserData = [];</span><br><span class="line"></span><br><span class="line"><span class="comment">//普通的logStuff函数，将内容打印到控制台     </span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">logStuff</span> (<span class="params">userData</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">if</span> ( <span class="keyword">typeof</span> userData === <span class="string">"string"</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(userData);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> ( <span class="keyword">typeof</span> userData === <span class="string">"object"</span>)&#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">var</span> item <span class="keyword">in</span> userData)&#123;</span><br><span class="line">            <span class="built_in">console</span>.log(item + <span class="string">": "</span> + userData[item]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125; </span><br><span class="line"></span><br><span class="line"><span class="comment">//一个接收两个参数的函数，后面一个是回调函数     </span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getInput</span> (<span class="params">options, callback</span>)</span>&#123;</span><br><span class="line">    allUserData.push(options);</span><br><span class="line">    callback(options);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//当我们调用getInput函数时，我们将logStuff作为一个参数传递给它     </span></span><br><span class="line"><span class="comment">//因此logStuff将会在getInput函数内被回调（或者执行）     </span></span><br><span class="line">getInput(&#123;<span class="attr">name</span>:<span class="string">"Rich"</span>,<span class="attr">speciality</span>:<span class="string">"Javascript"</span>&#125;, logStuff);</span><br><span class="line"><span class="comment">//name:Rich</span></span><br><span class="line"><span class="comment">//speciality:Javascript</span></span><br></pre></td></tr></table></figure>

<h4 id="传递参数给回调函数"><a href="#传递参数给回调函数" class="headerlink" title="传递参数给回调函数"></a>传递参数给回调函数</h4><p>既然回调函数在执行时仅仅是一个普通函数，我们就能给它传递参数。我们能够传递任何包含它的函数的属性（或者全局书讯给）作为回调函数的参数。在前面的例子中，我们将options作为一个参数传递给了毁掉函数。现在我们传递一个全局变量和一个本地变量：</p>
<h4 id="在执行之前确保回调函数是一个函数"><a href="#在执行之前确保回调函数是一个函数" class="headerlink" title="在执行之前确保回调函数是一个函数"></a>在执行之前确保回调函数是一个函数</h4><p>在调用之前检查作为参数被传递的回调函数确实是一个函数，这样的做法是明智的。同时，这也是一个实现条件回调函数的最佳时间。我们来重构上面例子中的getInput函数来确保检查是恰当的。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getInput</span>(<span class="params">options, callback</span>)</span>&#123;</span><br><span class="line">    allUserData.push(options);    </span><br><span class="line"></span><br><span class="line">    <span class="comment">//确保callback是一个函数    </span></span><br><span class="line">    <span class="keyword">if</span>(<span class="keyword">typeof</span> callback === <span class="string">"function"</span>)&#123;</span><br><span class="line">        <span class="comment">//调用它，既然我们已经确定了它是可调用的</span></span><br><span class="line">          callback(options);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="使用this对象的方法作为回调函数时的问题"><a href="#使用this对象的方法作为回调函数时的问题" class="headerlink" title="使用this对象的方法作为回调函数时的问题"></a>使用this对象的方法作为回调函数时的问题</h4><p>当回调函数是一个this对象的方法时，我们必须改变执行回调函数的方法来保证this对象的上下文。否则如果回调函数被传递给一个全局函数，this对象要么指向全局window对象（在浏览器中）。要么指向包含方法的对象。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> clientData = &#123;</span><br><span class="line">    id: <span class="number">094545</span>,</span><br><span class="line">    fullName : <span class="string">"Not Set"</span>,</span><br><span class="line">    <span class="comment">//setUsrName是一个在clientData对象中的方法</span></span><br><span class="line">    setUserName: fucntion (firstName, lastName)&#123;</span><br><span class="line">        <span class="comment">//这指向了对象中的fullName属性</span></span><br><span class="line">        <span class="keyword">this</span>.fullName = firstName + <span class="string">" "</span> + lastName;</span><br><span class="line">    &#125;</span><br><span class="line">&#125; </span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getUserInput</span>(<span class="params">firstName, lastName, callback</span>)</span>&#123;</span><br><span class="line">    <span class="comment">//在这做些什么来确认firstName/lastName</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//现在存储names</span></span><br><span class="line">    callback(firstName, lastName);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//在下面你的代码例子中，当clientData.setUsername被执行时，this.fullName并没有设置clientData对象中的fullName属性。相反，它将设置window对象中的fullName属性，因为getUserInput是一个全局函数。这是因为全局函数中的this对象指向window对象</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//全局函数中的thos都是指向window对象的</span></span><br><span class="line">getUserInput(<span class="string">"Barack"</span>,<span class="string">"Obama"</span>,clientData.setUserName);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(clientData,fullName);  <span class="comment">//Not Set</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//fullName属性将在window对象中被初始化     </span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">window</span>.fullName);  <span class="comment">//Barack Obama</span></span><br></pre></td></tr></table></figure>

<h4 id="使用Call和Apply来保存this"><a href="#使用Call和Apply来保存this" class="headerlink" title="使用Call和Apply来保存this"></a>使用Call和Apply来保存this</h4><p><strong>使用回调函数时一定要注意由于函数位置的改变，导致的this指针指向位置不同</strong></p>
<p>我们知道了每个Javascript中的函数都有两个方法:Call 和 Apply。这些方法被用来设置函数内部的this对象以及给此函数传递变量。</p>
<p>call接收的第一个参数为被用来在函数内部当做this的对象，传递给函数的参数被挨个传递（当然使用逗号分开）。Apply函数的第一个参数也是在函数内部作为this的对象，然而最后一个参数确是传递给函数的值的数组。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//注意到我们增加了新的参数作为回调对象，叫做“callbackObj”</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getUserInput</span>(<span class="params">firstName, lastName, callback. callbackObj</span>)</span>&#123;</span><br><span class="line">        <span class="comment">//在这里做些什么来确认名字</span></span><br><span class="line"></span><br><span class="line">        callback.apply(callbackObj, [firstName, lastName]);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//我们将clientData.setUserName方法和clientData对象作为参数，clientData对象会被Apply方法使用来设置this对象     </span></span><br><span class="line">getUserName(<span class="string">"Barack"</span>, <span class="string">"Obama"</span>, clientData.setUserName, clientData);</span><br><span class="line"></span><br><span class="line"><span class="comment">//clientData中的fullName属性被正确的设置</span></span><br><span class="line"><span class="built_in">console</span>.log(clientUser.fullName); <span class="comment">//Barack Obama</span></span><br></pre></td></tr></table></figure>

<h4 id="多重回调函数"><a href="#多重回调函数" class="headerlink" title="多重回调函数"></a>多重回调函数</h4><p>我们可以将不止一个的回调函数作为参数传递给一个函数，就像我们能够传递不止一个变量一样。</p>
<p>在执行异步代码时，无论以什么顺序简单的执行代码，经常情况会变成许多层级的回调函数堆积</p>
<ol>
<li>给你的函数命名并传递它们的名字作为回调函数，而不是主函数的参数中定义匿名函数。</li>
<li>模块化L将你的代码分隔到模块中，这样你就可以到处一块代码来完成特定的工作。然后你可以在你的巨型应用中导入模块</li>
</ol>
<h2 id="Promise基本用法"><a href="#Promise基本用法" class="headerlink" title="Promise基本用法"></a>Promise基本用法</h2><p><code>Promise</code>对象是一个构造函数，用来生成<code>Promise</code>实例</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> promise = <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span>(<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// ... some code</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (<span class="comment">/* 异步操作成功 */</span>)&#123;</span><br><span class="line">    resolve(value);</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    reject(error);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">//Promise构造函数接受一个函数作为参数，该函数的两个参数分别是resolve和reject。它们是两个函数，由 JavaScript 引擎提供，不用自己部署。</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//resolve函数的作用是，将Promise对象的状态从“未完成”变为“成功”（即从 pending 变为 resolved），在异步操作成功时调用，并将异步操作的结果，作为参数传递出去；reject函数的作用是，将Promise对象的状态从“未完成”变为“失败”（即从 pending 变为 rejected），在异步操作失败时调用，并将异步操作报出的错误，作为参数传递出去。</span></span><br><span class="line"><span class="comment">//Promise实例生成以后，可以用then方法分别指定resolved状态和rejected状态的回调函数。</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//Promise实例生成以后，可以用then方法分别指定resolved状态和rejected状态的回调函数。</span></span><br><span class="line">promise.then(<span class="function"><span class="keyword">function</span>(<span class="params">value</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// success</span></span><br><span class="line">&#125;, <span class="function"><span class="keyword">function</span>(<span class="params">error</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// failure</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">//then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用，第二个回调函数是Promise对象的状态变为rejected时调用。其中，第二个函数是可选的，不一定要提供。这两个函数都接受Promise对象传出的值作为参数。</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> promise = <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span>(<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'Promise'</span>);</span><br><span class="line">  resolve();</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">promise.then(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'resolved.'</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'Hi!'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Promise</span></span><br><span class="line"><span class="comment">// Hi!</span></span><br><span class="line"><span class="comment">// resolved</span></span><br><span class="line"><span class="comment">//Promise 新建后就会立即执行，首先输出的是Promise。然后，then方法指定的回调函数，将在当前脚本所有同步任务执行完才会执行，所以resolved最后输出。</span></span><br></pre></td></tr></table></figure>

<p>下面我们写异步加载图片和实现Ajax操作的例子。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">loadImageAsync</span>(<span class="params">url</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span>(<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> image = <span class="keyword">new</span> Image();</span><br><span class="line"></span><br><span class="line">    image.onload = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">      resolve(image);</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    image.onerror = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">      reject(<span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'Could not load image at '</span> + url));</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    image.src = url;</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//上面代码中，使用Promise包装了一个图片加载的异步操作。如果加载成功，就调用resolve方法，否则就调用reject方法。</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> getJSON = <span class="function"><span class="keyword">function</span>(<span class="params">url</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> promise = <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span>(<span class="params">resolve, reject</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">const</span> handler = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">      <span class="keyword">if</span> (<span class="keyword">this</span>.readyState !== <span class="number">4</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> (<span class="keyword">this</span>.status === <span class="number">200</span>) &#123;</span><br><span class="line">        resolve(<span class="keyword">this</span>.response);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        reject(<span class="keyword">new</span> <span class="built_in">Error</span>(<span class="keyword">this</span>.statusText));</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="keyword">const</span> client = <span class="keyword">new</span> XMLHttpRequest();</span><br><span class="line">    client.open(<span class="string">"GET"</span>, url);</span><br><span class="line">    client.onreadystatechange = handler;</span><br><span class="line">    client.responseType = <span class="string">"json"</span>;</span><br><span class="line">    client.setRequestHeader(<span class="string">"Accept"</span>, <span class="string">"application/json"</span>);</span><br><span class="line">    client.send();</span><br><span class="line"></span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> promise;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">getJSON(<span class="string">"/posts.json"</span>).then(<span class="function"><span class="keyword">function</span>(<span class="params">json</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'Contents: '</span> + json);</span><br><span class="line">&#125;, <span class="function"><span class="keyword">function</span>(<span class="params">error</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.error(<span class="string">'出错了'</span>, error);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">//上面代码中，getJSON是对 XMLHttpRequest 对象的封装，用于发出一个针对 JSON 数据的 HTTP 请求，并且返回一个Promise对象。需要注意的是，在getJSON内部，resolve函数和reject函数调用时，都带有参数。</span></span><br><span class="line"><span class="comment">//如果调用resolve函数和reject函数时带有参数，那么它们的参数会被传递给回调函数。reject函数的参数通常是Error对象的实例，表示抛出的错误；resolve函数的参数除了正常的值以外，还可能是另一个 Promise 实例，</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> p1 = <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span> (<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> p2 = <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span> (<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  resolve(p1);</span><br><span class="line">&#125;)</span><br><span class="line"><span class="comment">//一个异步操作p2的结果是返回另一个异步操作p1，这时p1的状态就会传递给p2，也就是说，p1的状态决定了p2的状态。如果p1的状态是pending，那么p2的回调函数就会等待p1的状态改变；如果p1的状态已经是resolved或者rejected，那么p2的回调函数将会立刻执行。</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> p1 = <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span> (<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">  setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> reject(<span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'fail'</span>)), <span class="number">3000</span>)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> p2 = <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span> (<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">  setTimeout(<span class="function"><span class="params">()</span> =&gt;</span> resolve(p1), <span class="number">1000</span>)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">p2</span><br><span class="line">  .then(<span class="function"><span class="params">result</span> =&gt;</span> <span class="built_in">console</span>.log(result))</span><br><span class="line">  .catch(<span class="function"><span class="params">error</span> =&gt;</span> <span class="built_in">console</span>.log(error))</span><br><span class="line"><span class="comment">// Error: fail</span></span><br><span class="line"><span class="comment">//上面代码中，p1是一个 Promise，3 秒之后变为rejected。p2的状态在 1 秒之后改变，resolve方法返回的是p1。由于p2返回的是另一个 Promise，导致p2自己的状态无效了，由p1的状态决定p2的状态。所以，后面的then语句都变成针对后者（p1）。又过了 2 秒，p1变为rejected，导致触发catch方法指定的回调函数。</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//调用resolve或reject并不会终结 Promise 的参数函数的执行。</span></span><br><span class="line"><span class="comment">//一般来说，调用resolve或reject以后，Promise 的使命就完成了，后继操作应该放到then方法里面，而不应该直接写在resolve或reject的后面。所以，最好在它们前面加上return语句，这样就不会有意外。</span></span><br><span class="line"><span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> resolve(<span class="number">1</span>);</span><br><span class="line">  <span class="comment">// 后面的语句不会执行</span></span><br><span class="line">  <span class="built_in">console</span>.log(<span class="number">2</span>);</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>





<h2 id="Promise的各类方法使用"><a href="#Promise的各类方法使用" class="headerlink" title="Promise的各类方法使用"></a>Promise的各类方法使用</h2><h3 id="then方法"><a href="#then方法" class="headerlink" title="then方法"></a>then方法</h3><p>Promise 实例具有<code>then</code>方法，也就是说，<code>then</code>方法是定义在原型对象<code>Promise.prototype</code>上的。它的作用是为 Promise 实例添加状态改变时的回调函数。<code>then</code>方法返回的是一个新的<code>Promise</code>实例（注意，不是原来那个<code>Promise</code>实例）。因此可以采用链式写法，即<code>then</code>方法后面再调用另一个<code>then</code>方法。</p>
<p>第一个回调函数完成以后，会将返回结果作为参数，传入第二个回调函数。采用链式的<code>then</code>，可以指定一组按照次序调用的回调函数。这时，前一个回调函数，有可能返回的还是一个<code>Promise</code>对象（即有异步操作），这时后一个回调函数，就会等待该<code>Promise</code>对象的状态发生变化，才会被调用。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">getJSON(<span class="string">"/post/1.json"</span>).then(<span class="function"><span class="keyword">function</span>(<span class="params">post</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> getJSON(post.commentURL);</span><br><span class="line">&#125;).then(<span class="function"><span class="keyword">function</span> (<span class="params">comments</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">"resolved: "</span>, comments);</span><br><span class="line">&#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err</span>)</span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">"rejected: "</span>, err);</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">//上面代码中，第一个then方法指定的回调函数，返回的是另一个Promise对象。这时，第二个then方法指定的回调函数，就会等待这个新的Promise对象状态发生变化。如果变为resolved，就调用第一个回调函数，如果状态变为rejected，就调用第二个回调函数</span></span><br></pre></td></tr></table></figure>



<h3 id="catch方法"><a href="#catch方法" class="headerlink" title="catch方法"></a>catch方法</h3><p><code>Promise.prototype.catch()</code>方法是<code>.then(null, rejection)</code>或<code>.then(undefined, rejection)</code>的别名，用于指定发生错误时的回调函数。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line">getJSON(<span class="string">'/posts.json'</span>).then(<span class="function"><span class="keyword">function</span>(<span class="params">posts</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;).catch(<span class="function"><span class="keyword">function</span>(<span class="params">error</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 处理 getJSON 和 前一个回调函数运行时发生的错误</span></span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'发生错误！'</span>, error);</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">//上面代码中，getJSON()方法返回一个 Promise 对象，如果该对象状态变为resolved，则会调用then()方法指定的回调函数；如果异步操作抛出错误，状态就会变为rejected，就会调用catch()方法指定的回调函数，处理这个错误。另外，then()方法指定的回调函数，如果运行中抛出错误，也会被catch()方法捕获。</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 写法一</span></span><br><span class="line"><span class="keyword">const</span> promise = <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span>(<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'test'</span>);</span><br><span class="line">  &#125; <span class="keyword">catch</span>(e) &#123;</span><br><span class="line">    reject(e);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br><span class="line">promise.catch(<span class="function"><span class="keyword">function</span>(<span class="params">error</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(error);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 写法二</span></span><br><span class="line"><span class="keyword">const</span> promise = <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span>(<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">  reject(<span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'test'</span>));</span><br><span class="line">&#125;);</span><br><span class="line">promise.catch(<span class="function"><span class="keyword">function</span>(<span class="params">error</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(error);</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">//这两种写法是等价的，reject()方法的作用等同于抛出错误；</span></span><br><span class="line"><span class="comment">//如果promise状态已经变成resolved，再抛出错误是无效的Promise 在resolve语句后面，再抛出错误，不会被捕获，等于没有抛出。因为 Promise 的状态一旦改变，就永久保持该状态，不会再变了</span></span><br><span class="line"><span class="comment">//Promise 对象的错误具有“冒泡”性质，会一直向后传递，直到被捕获为止。也就是说，错误总是会被下一个catch语句捕获</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//一般来说，不要在then()方法里面定义 Reject 状态的回调函数（即then的第二个参数），总是使用catch方法。</span></span><br><span class="line"><span class="comment">// bad</span></span><br><span class="line">promise</span><br><span class="line">  .then(<span class="function"><span class="keyword">function</span>(<span class="params">data</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// success</span></span><br><span class="line">  &#125;, <span class="function"><span class="keyword">function</span>(<span class="params">err</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// error</span></span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// good</span></span><br><span class="line">promise</span><br><span class="line">  .then(<span class="function"><span class="keyword">function</span>(<span class="params">data</span>) </span>&#123; <span class="comment">//cb</span></span><br><span class="line">    <span class="comment">// success</span></span><br><span class="line">  &#125;)</span><br><span class="line">  .catch(<span class="function"><span class="keyword">function</span>(<span class="params">err</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// error</span></span><br><span class="line">  &#125;);</span><br><span class="line"><span class="comment">//因为第二种写法可以捕获前面then方法中执行的错误，也·更接近同步的写法（try、catch），因此建议总是使用catch()方法</span></span><br></pre></td></tr></table></figure>

<p>跟传统的<code>try/catch</code>代码块不同的是，如果没有使用<code>catch()</code>方法指定错误处理的回调函数，Promise 对象抛出的错误不会传递到外层代码，即不会有任何反应。Promise 内部的错误不会影响到 Promise 外部的代码，通俗的说法就是“Promise 会吃掉错误”。</p>
<p>不过，Node.js 有一个<code>unhandledRejection</code>事件，专门监听未捕获的<code>reject</code>错误，上面的脚本会触发这个事件的监听函数，可以在监听函数里面抛出错误。</p>
<p>一般总是建议，Promise 对象后面要跟<code>catch()</code>方法，这样可以处理 Promise 内部发生的错误。<code>catch()</code>方法返回的还是一个 Promise 对象，因此后面还可以接着调用<code>then()</code>方法。</p>
<h3 id="finally方法"><a href="#finally方法" class="headerlink" title="finally方法"></a>finally方法</h3><p><code>finally()</code>方法用于指定不管 Promise 对象最后状态如何，都会执行的操作。finally方法的回调函数不接受任何参数，这意味着没有办法知道，前面的 Promise 状态到底是fulfilled还是rejected。这表明，<code>finally</code>方法里面的操作，应该是与状态无关的，不依赖于 Promise 的执行结果。</p>
<p><code>finally</code>本质上是<code>then</code>方法的特例。如果不使用<code>finally</code>方法，同样的语句需要为成功和失败两种情况各写一次。有了<code>finally</code>方法，则只需要写一次。</p>
<p>finally方法的实现：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Promise</span>.prototype.finally = <span class="function"><span class="keyword">function</span> (<span class="params">callback</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> P = <span class="keyword">this</span>.constructor;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">this</span>.then(</span><br><span class="line">    value  =&gt; P.resolve(callback()).then(<span class="function"><span class="params">()</span> =&gt;</span> value),</span><br><span class="line">    reason =&gt; P.resolve(callback()).then(<span class="function"><span class="params">()</span> =&gt;</span> &#123; <span class="keyword">throw</span> reason &#125;)</span><br><span class="line">  );</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>



<h3 id="all方法"><a href="#all方法" class="headerlink" title="all方法"></a>all方法</h3><p><code>Promise.all()</code>方法用于将多个 Promise 实例，包装成一个新的 Promise 实例。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> p = <span class="built_in">Promise</span>.all([p1, p2, p3]);</span><br></pre></td></tr></table></figure>

<p>上面代码中，<code>Promise.all()</code>方法接受一个数组作为参数，<code>p1</code>、<code>p2</code>、<code>p3</code>都是 Promise 实例，如果不是，就会先调用下面讲到的<code>Promise.resolve</code>方法，将参数转为 Promise 实例，再进一步处理。另外，<code>Promise.all()</code>方法的参数可以不是数组，但必须具有 Iterator 接口，且返回的每个成员都是 Promise 实例。p的状态由<code>p1</code>、<code>p2</code>、<code>p3</code>决定，分成两种情况。</p>
<p>（1）只有<code>p1</code>、<code>p2</code>、<code>p3</code>的状态都变成<code>fulfilled</code>，<code>p</code>的状态才会变成<code>fulfilled</code>，此时<code>p1</code>、<code>p2</code>、<code>p3</code>的返回值组成一个数组，传递给<code>p</code>的回调函数。</p>
<p>（2）只要<code>p1</code>、<code>p2</code>、<code>p3</code>之中有一个被<code>rejected</code>，<code>p</code>的状态就变成<code>rejected</code>，此时第一个被<code>reject</code>的实例的返回值，会传递给<code>p</code>的回调函数。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> databasePromise = connectDatabase();</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> booksPromise = databasePromise</span><br><span class="line">  .then(findAllBooks);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> userPromise = databasePromise</span><br><span class="line">  .then(getCurrentUser);</span><br><span class="line"></span><br><span class="line"><span class="built_in">Promise</span>.all([</span><br><span class="line">  booksPromise,</span><br><span class="line">  userPromise</span><br><span class="line">])</span><br><span class="line">.then(<span class="function">(<span class="params">[books, user]</span>) =&gt;</span> pickTopRecommendations(books, user));</span><br></pre></td></tr></table></figure>

<p>上面代码中，<code>booksPromise</code>和<code>userPromise</code>是两个异步操作，只有等到它们的结果都返回了，才会触发<code>pickTopRecommendations</code>这个回调函数。</p>
<p>注意，如果作为参数的 Promise 实例，自己定义了<code>catch</code>方法，那么它一旦被<code>rejected</code>，并不会触发<code>Promise.all()</code>的<code>catch</code>方法。</p>
<h3 id="race方法"><a href="#race方法" class="headerlink" title="race方法"></a>race方法</h3><p><code>Promise.race()</code>方法同样是将多个 Promise 实例，包装成一个新的 Promise 实例。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> p = <span class="built_in">Promise</span>.race([p1, p2, p3]);</span><br></pre></td></tr></table></figure>

<p>上面代码中，只要<code>p1</code>、<code>p2</code>、<code>p3</code>之中有一个实例率先改变状态，<code>p</code>的状态就跟着改变。那个率先改变的 Promise 实例的返回值，就传递给<code>p</code>的回调函数。</p>
<p><code>Promise.race()</code>方法的参数与<code>Promise.all()</code>方法一样，如果不是 Promise 实例，就会先调用下面讲到的<code>Promise.resolve()</code>方法，将参数转为 Promise 实例，再进一步处理。</p>
<h2 id="promise的链式调用"><a href="#promise的链式调用" class="headerlink" title="promise的链式调用"></a>promise的链式调用</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">start</span>(<span class="params"></span>) </span>&#123;  </span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;  </span><br><span class="line">      resolve(<span class="string">'start'</span>);  </span><br><span class="line">    &#125;);  </span><br><span class="line">  &#125;  </span><br><span class="line"></span><br><span class="line">  start()  </span><br><span class="line">    .then(<span class="function"><span class="params">data</span> =&gt;</span> &#123;  </span><br><span class="line">      <span class="comment">// promise start  </span></span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">'result of start: '</span>, data);  </span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">Promise</span>.resolve(<span class="number">1</span>); <span class="comment">// p1  </span></span><br><span class="line">    &#125;)  </span><br><span class="line">    .then(<span class="function"><span class="params">data</span> =&gt;</span> &#123;  </span><br><span class="line">      <span class="comment">// promise p1  </span></span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">'result of p1: '</span>, data);  </span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">Promise</span>.reject(<span class="number">2</span>); <span class="comment">// p2  </span></span><br><span class="line">    &#125;)  </span><br><span class="line">    .then(<span class="function"><span class="params">data</span> =&gt;</span> &#123;  </span><br><span class="line">      <span class="comment">// promise p2  </span></span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">'result of p2: '</span>, data);  </span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">Promise</span>.resolve(<span class="number">3</span>); <span class="comment">// p3  </span></span><br><span class="line">    &#125;)  </span><br><span class="line">    .catch(<span class="function"><span class="params">ex</span> =&gt;</span> &#123;  </span><br><span class="line">      <span class="comment">// promise p3  </span></span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">'ex: '</span>, ex);  </span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">Promise</span>.resolve(<span class="number">4</span>); <span class="comment">// p4  </span></span><br><span class="line">    &#125;)  </span><br><span class="line">    .then(<span class="function"><span class="params">data</span> =&gt;</span> &#123;  </span><br><span class="line">      <span class="comment">// promise p4  </span></span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">'result of p4: '</span>, data);  </span><br><span class="line">    &#125;);</span><br></pre></td></tr></table></figure>

<p>promise俗称链式调用，它是es6中最重要的特性之一<br>简单的说可以不停的then调用嵌套在调用（异步之后，链式调用方式执行回调），这种操作方式称为promise</p>
<p>⑴． resolved（全部置为完成状态）<br>    ①.初始化：比如说以国家，省份，县市（china ，jiangshu ，xian）三个方法来演示下链式调用关系（采用setTimeout模拟异步操作）</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"> <span class="function"><span class="keyword">function</span>  <span class="title">china</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">                    <span class="built_in">console</span>.log(<span class="string">'china中国'</span>)</span><br><span class="line">                    <span class="keyword">var</span> p =<span class="keyword">new</span> <span class="built_in">Promise</span>(</span><br><span class="line">                    <span class="function"><span class="keyword">function</span>(<span class="params"> resolve,reject </span>) </span>&#123;</span><br><span class="line">                             setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">                             <span class="built_in">console</span>.log(<span class="string">'中国  国家'</span>)</span><br><span class="line">                             resolve(<span class="string">'教育大省份'</span>)</span><br><span class="line">                         &#125;,<span class="number">1000</span>)</span><br><span class="line">                    &#125;</span><br><span class="line">               )</span><br><span class="line">               <span class="keyword">return</span>  p;</span><br><span class="line">           &#125;</span><br><span class="line"> </span><br><span class="line">           <span class="function"><span class="keyword">function</span>  <span class="title">jiangshu</span>(<span class="params">data</span>)</span>&#123;</span><br><span class="line">                    <span class="built_in">console</span>.log(<span class="string">'江苏'</span>+data);</span><br><span class="line">                    <span class="keyword">var</span> p=<span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span>(<span class="params">resolve,reject</span>)</span>&#123;</span><br><span class="line">                         setTimeout(<span class="function"><span class="keyword">function</span> (<span class="params"></span>)</span>&#123;</span><br><span class="line">                         <span class="built_in">console</span>.log(<span class="string">'江苏 省份'</span>)</span><br><span class="line">                         resolve(<span class="string">'地级市'</span>);</span><br><span class="line">                    &#125;,<span class="number">2000</span>)</span><br><span class="line">               &#125;)</span><br><span class="line">               <span class="keyword">return</span> p;</span><br><span class="line">           &#125;</span><br><span class="line"> </span><br><span class="line">           <span class="function"><span class="keyword">function</span>  <span class="title">xian</span>(<span class="params">data</span>)</span>&#123;</span><br><span class="line">                   <span class="built_in">console</span>.log(<span class="string">'盱眙县'</span>+data)</span><br><span class="line">                    <span class="keyword">var</span> p=<span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span>(<span class="params">resolve,reject</span>)</span>&#123;</span><br><span class="line">                         setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">                         <span class="built_in">console</span>.log(<span class="string">'盱眙县'</span>);</span><br><span class="line">                         resolve (<span class="string">'淮河镇'</span>)</span><br><span class="line">                    &#125;,<span class="number">2000</span>)</span><br><span class="line">               &#125;)</span><br><span class="line">               <span class="keyword">return</span> p;</span><br><span class="line">            &#125;</span><br><span class="line">china ().then(jiangshu).then(xian).then(<span class="function"><span class="keyword">function</span>(<span class="params">data</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(data)</span><br><span class="line">&#125;)</span><br><span class="line"><span class="comment">/*输出：china 中国</span></span><br><span class="line"><span class="comment">中国 国家</span></span><br><span class="line"><span class="comment">江苏教育大省份</span></span><br><span class="line"><span class="comment">江苏 省份</span></span><br><span class="line"><span class="comment">盱眙县地级市</span></span><br><span class="line"><span class="comment">盱眙县</span></span><br><span class="line"><span class="comment">淮河镇</span></span><br></pre></td></tr></table></figure>

<p><em><strong>*rejected（部分置为无效状态）*</strong></em><br>  <strong>①.初始化：同样的以上述的函数为例</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span>  <span class="title">china</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">                    <span class="built_in">console</span>.log(<span class="string">'china中国'</span>)</span><br><span class="line">                    <span class="keyword">var</span> p =<span class="keyword">new</span> <span class="built_in">Promise</span>(</span><br><span class="line">                    <span class="function"><span class="keyword">function</span>(<span class="params"> resolve,reject </span>) </span>&#123;</span><br><span class="line">                             setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">                             <span class="built_in">console</span>.log(<span class="string">'中国  国家'</span>)</span><br><span class="line">                             reject(<span class="string">'教育大省份'</span>)</span><br><span class="line">                         &#125;,<span class="number">1000</span>)</span><br><span class="line">                    &#125;</span><br><span class="line">               )</span><br><span class="line">               <span class="keyword">return</span>  p;</span><br><span class="line">                 &#125;</span><br><span class="line">              </span><br><span class="line">                 <span class="function"><span class="keyword">function</span> <span class="title">jiangshu</span>(<span class="params">data</span>)</span>&#123;</span><br><span class="line">                       <span class="built_in">console</span>.log(<span class="string">'江苏是'</span>+data);</span><br><span class="line">                       varp=<span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span>(<span class="params">resolve,reject</span>)</span>&#123;</span><br><span class="line">                            setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">                            <span class="built_in">console</span>.log(<span class="string">'江苏 省份'</span>)</span><br><span class="line">                            resolve(<span class="string">'地级市'</span>);</span><br><span class="line">                       &#125;,<span class="number">2000</span>)</span><br><span class="line">                   &#125;)</span><br><span class="line">                   returnp;</span><br><span class="line">              &#125;</span><br><span class="line"><span class="comment">//函数写完之后，就开始结合then来链式调用了</span></span><br><span class="line">china()</span><br><span class="line">         .then(jiangshu,<span class="function"><span class="keyword">function</span>(<span class="params">data</span>)</span>&#123; <span class="built_in">console</span>.log(data)&#125;)</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 等同于（null不执行）</span></span><br><span class="line">         china()</span><br><span class="line">         .then(<span class="literal">null</span>,<span class="function"><span class="keyword">function</span>(<span class="params">data</span>)</span>&#123; <span class="built_in">console</span>.log(data)&#125;)</span><br><span class="line"> </span><br><span class="line">         <span class="comment">//等同于（直接执行catch回调，抛出异常，页面也不会卡死，直接走catch）</span></span><br><span class="line">         china()</span><br><span class="line">         .then(jiangshu).catch(<span class="function"><span class="keyword">function</span>(<span class="params">data</span>)</span>&#123;<span class="built_in">console</span>.log(data)&#125;)</span><br><span class="line"><span class="comment">//(备注：为reject的时候，执行then的第二个参数回调，不会执行jiangshu)</span></span><br><span class="line"><span class="comment">//控制台输出：china 中国</span></span><br><span class="line"><span class="comment">//中国 国家</span></span><br><span class="line"><span class="comment">//教育大省份</span></span><br></pre></td></tr></table></figure>


      
    </div>
    
    
    
    
    <div>
      
    </div>
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  


  
  <nav class="pagination">
    <a class="extend prev" rel="prev" href="/page/7/"><i class="fa fa-angle-left" aria-label="上一页"></i></a><a class="page-number" href="/">1</a><span class="space">&hellip;</span><a class="page-number" href="/page/7/">7</a><span class="page-number current">8</span><a class="page-number" href="/page/9/">9</a><a class="page-number" href="/page/10/">10</a><a class="extend next" rel="next" href="/page/9/"><i class="fa fa-angle-right" aria-label="下一页"></i></a>
  </nav>



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="Li Yudong"
      src="/images/author.jpg">
  <p class="site-author-name" itemprop="name">Li Yudong</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">95</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">14</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/tags/">
          
        <span class="site-state-item-count">18</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="https://github.com/tiarmor1" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;tiarmor1" rel="noopener" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
      <span class="links-of-author-item">
        <a href="mailto:1157019137@qq.com" title="E-Mail → mailto:1157019137@qq.com" rel="noopener" target="_blank"><i class="fa fa-envelope fa-fw"></i>E-Mail</a>
      </span>
  </div>
  <div class="cc-license motion-element" itemprop="license">
    <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" class="cc-opacity" rel="noopener" target="_blank"><img src="/images/cc-by-nc-sa.svg" alt="Creative Commons"></a>
  </div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 2020 – 
  <span itemprop="copyrightYear">2022</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Li Yudong</span>
</div>

        








      </div>
    </footer>
  </div>

  
  
  <script color='0,0,0' opacity='0.5' zIndex='-1' count='150' src="/lib1/canvas-nest/canvas-nest.min.js"></script>
  <script src="/lib1/anime.min.js"></script>
  <script src="/lib1/velocity/velocity.min.js"></script>
  <script src="/lib1/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/pisces.js"></script>


<script src="/js/next-boot.js"></script>




  




  
<script src="/js/local-search.js"></script>













  

  

  
   <canvas class="fireworks" style="position: fixed;left: 0;top: 0;z-index: 1; pointer-events: none;" ></canvas> 
   <script type="text/javascript" src="//cdn.bootcss.com/animejs/2.2.0/anime.min.js"></script> 
   <script type="text/javascript" src="/js/fireworks.js"></script>
  
</body>
</html>

